From 232076b41d310050deefcba6e0f65b5505fada01 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 31 Aug 2018 12:59:39 +0200 Subject: [PATCH 001/357] Update frontend to 20180831.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index da3d225bba0..5508aa76acf 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -26,7 +26,7 @@ from homeassistant.helpers.translation import async_get_translations from homeassistant.loader import bind_hass from homeassistant.util.yaml import load_yaml -REQUIREMENTS = ['home-assistant-frontend==20180829.1'] +REQUIREMENTS = ['home-assistant-frontend==20180831.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index dad6a274676..6e9cbeffc7d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -442,7 +442,7 @@ hole==0.3.0 holidays==0.9.6 # homeassistant.components.frontend -home-assistant-frontend==20180829.1 +home-assistant-frontend==20180831.0 # homeassistant.components.homekit_controller # homekit==0.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index feb9b6d5efa..57d4dd9b996 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -84,7 +84,7 @@ hbmqtt==0.9.2 holidays==0.9.6 # homeassistant.components.frontend -home-assistant-frontend==20180829.1 +home-assistant-frontend==20180831.0 # homeassistant.components.homematicip_cloud homematicip==0.9.8 From 8be7a0a9b966334ea30655dfd937b27f51b092c2 Mon Sep 17 00:00:00 2001 From: lamiskin Date: Fri, 31 Aug 2018 20:57:07 +1000 Subject: [PATCH 002/357] Correct wemo static device discovery issue. (#16292) A recent change caused an issue if a single static wemo device is offline and could not be reached, then the whole component would not initialize (and therefore all other wemo devices are not added). --- homeassistant/components/wemo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/wemo.py b/homeassistant/components/wemo.py index 2ce2ff475a2..9664ca9419a 100644 --- a/homeassistant/components/wemo.py +++ b/homeassistant/components/wemo.py @@ -124,14 +124,14 @@ def setup(hass, config): _LOGGER.error( 'Unable to get description url for %s', '{}:{}'.format(host, port) if port else host) - return False + continue try: device = pywemo.discovery.device_from_description(url, None) except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as err: _LOGGER.error('Unable to access %s (%s)', url, err) - return False + continue devices.append((url, device)) From 16a58bd1cf0087dc793f25bd48d00b50d3b0459c Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Fri, 31 Aug 2018 10:17:11 +0200 Subject: [PATCH 003/357] Fix LIFX effects (#16309) --- CODEOWNERS | 3 + homeassistant/components/light/lifx.py | 125 ++++++++++++------------- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index c756cb383d4..b86e09a6b72 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -52,6 +52,8 @@ homeassistant/components/cover/template.py @PhracturedBlue homeassistant/components/device_tracker/automatic.py @armills homeassistant/components/device_tracker/tile.py @bachya homeassistant/components/history_graph.py @andrey-git +homeassistant/components/light/lifx.py @amelchio +homeassistant/components/light/lifx_legacy.py @amelchio homeassistant/components/light/tplink.py @rytilahti homeassistant/components/light/yeelight.py @rytilahti homeassistant/components/lock/nello.py @pschmitt @@ -65,6 +67,7 @@ homeassistant/components/media_player/sonos.py @amelchio homeassistant/components/media_player/xiaomi_tv.py @fattdev homeassistant/components/media_player/yamaha_musiccast.py @jalmeroth homeassistant/components/plant.py @ChristianKuehnel +homeassistant/components/scene/lifx_cloud.py @amelchio homeassistant/components/sensor/airvisual.py @bachya homeassistant/components/sensor/filter.py @dgomes homeassistant/components/sensor/gearbest.py @HerrHofrat diff --git a/homeassistant/components/light/lifx.py b/homeassistant/components/light/lifx.py index cf5d6fef704..bea39354e1b 100644 --- a/homeassistant/components/light/lifx.py +++ b/homeassistant/components/light/lifx.py @@ -167,9 +167,9 @@ async def async_setup_platform(hass, return True -def lifx_features(device): - """Return a feature map for this device, or a default map if unknown.""" - return aiolifx().products.features_map.get(device.product) or \ +def lifx_features(bulb): + """Return a feature map for this bulb, or a default map if unknown.""" + return aiolifx().products.features_map.get(bulb.product) or \ aiolifx().products.features_map.get(1) @@ -256,7 +256,7 @@ class LIFXManager: async def start_effect(self, entities, service, **kwargs): """Start a light effect on entities.""" - devices = [light.device for light in entities] + bulbs = [light.bulb for light in entities] if service == SERVICE_EFFECT_PULSE: effect = aiolifx_effects().EffectPulse( @@ -266,7 +266,7 @@ class LIFXManager: mode=kwargs.get(ATTR_MODE), hsbk=find_hsbk(**kwargs), ) - await self.effects_conductor.start(effect, devices) + await self.effects_conductor.start(effect, bulbs) elif service == SERVICE_EFFECT_COLORLOOP: preprocess_turn_on_alternatives(kwargs) @@ -282,12 +282,12 @@ class LIFXManager: transition=kwargs.get(ATTR_TRANSITION), brightness=brightness, ) - await self.effects_conductor.start(effect, devices) + await self.effects_conductor.start(effect, bulbs) elif service == SERVICE_EFFECT_STOP: - await self.effects_conductor.stop(devices) + await self.effects_conductor.stop(bulbs) def service_to_entities(self, service): - """Return the known devices that a service call mentions.""" + """Return the known entities that a service call mentions.""" entity_ids = extract_entity_ids(self.hass, service) if entity_ids: entities = [entity for entity in self.entities.values() @@ -298,50 +298,50 @@ class LIFXManager: return entities @callback - def register(self, device): + def register(self, bulb): """Handle aiolifx detected bulb.""" - self.hass.async_add_job(self.register_new_device(device)) + self.hass.async_add_job(self.register_new_bulb(bulb)) - async def register_new_device(self, device): + async def register_new_bulb(self, bulb): """Handle newly detected bulb.""" - if device.mac_addr in self.entities: - entity = self.entities[device.mac_addr] + if bulb.mac_addr in self.entities: + entity = self.entities[bulb.mac_addr] entity.registered = True _LOGGER.debug("%s register AGAIN", entity.who) await entity.update_hass() else: - _LOGGER.debug("%s register NEW", device.ip_addr) + _LOGGER.debug("%s register NEW", bulb.ip_addr) # Read initial state ack = AwaitAioLIFX().wait - color_resp = await ack(device.get_color) + color_resp = await ack(bulb.get_color) if color_resp: - version_resp = await ack(device.get_version) + version_resp = await ack(bulb.get_version) if color_resp is None or version_resp is None: - _LOGGER.error("Failed to initialize %s", device.ip_addr) - device.registered = False + _LOGGER.error("Failed to initialize %s", bulb.ip_addr) + bulb.registered = False else: - device.timeout = MESSAGE_TIMEOUT - device.retry_count = MESSAGE_RETRIES - device.unregister_timeout = UNAVAILABLE_GRACE + bulb.timeout = MESSAGE_TIMEOUT + bulb.retry_count = MESSAGE_RETRIES + bulb.unregister_timeout = UNAVAILABLE_GRACE - if lifx_features(device)["multizone"]: - entity = LIFXStrip(device, self.effects_conductor) - elif lifx_features(device)["color"]: - entity = LIFXColor(device, self.effects_conductor) + if lifx_features(bulb)["multizone"]: + entity = LIFXStrip(bulb, self.effects_conductor) + elif lifx_features(bulb)["color"]: + entity = LIFXColor(bulb, self.effects_conductor) else: - entity = LIFXWhite(device, self.effects_conductor) + entity = LIFXWhite(bulb, self.effects_conductor) _LOGGER.debug("%s register READY", entity.who) - self.entities[device.mac_addr] = entity + self.entities[bulb.mac_addr] = entity self.async_add_entities([entity], True) @callback - def unregister(self, device): + def unregister(self, bulb): """Handle aiolifx disappearing bulbs.""" - if device.mac_addr in self.entities: - entity = self.entities[device.mac_addr] + if bulb.mac_addr in self.entities: + entity = self.entities[bulb.mac_addr] _LOGGER.debug("%s unregister", entity.who) entity.registered = False self.hass.async_add_job(entity.async_update_ha_state()) @@ -352,20 +352,17 @@ class AwaitAioLIFX: def __init__(self): """Initialize the wrapper.""" - self.device = None self.message = None self.event = asyncio.Event() @callback - def callback(self, device, message): + def callback(self, bulb, message): """Handle responses.""" - self.device = device self.message = message self.event.set() async def wait(self, method): """Call an aiolifx method and wait for its response.""" - self.device = None self.message = None self.event.clear() method(callb=self.callback) @@ -387,9 +384,9 @@ def convert_16_to_8(value): class LIFXLight(Light): """Representation of a LIFX light.""" - def __init__(self, device, effects_conductor): + def __init__(self, bulb, effects_conductor): """Initialize the light.""" - self.light = device + self.bulb = bulb self.effects_conductor = effects_conductor self.registered = True self.postponed_update = None @@ -397,34 +394,34 @@ class LIFXLight(Light): @property def available(self): - """Return the availability of the device.""" + """Return the availability of the bulb.""" return self.registered @property def unique_id(self): """Return a unique ID.""" - return self.light.mac_addr + return self.bulb.mac_addr @property def name(self): - """Return the name of the device.""" - return self.light.label + """Return the name of the bulb.""" + return self.bulb.label @property def who(self): - """Return a string identifying the device.""" - return "%s (%s)" % (self.light.ip_addr, self.name) + """Return a string identifying the bulb.""" + return "%s (%s)" % (self.bulb.ip_addr, self.name) @property def min_mireds(self): """Return the coldest color_temp that this light supports.""" - kelvin = lifx_features(self.light)['max_kelvin'] + kelvin = lifx_features(self.bulb)['max_kelvin'] return math.floor(color_util.color_temperature_kelvin_to_mired(kelvin)) @property def max_mireds(self): """Return the warmest color_temp that this light supports.""" - kelvin = lifx_features(self.light)['min_kelvin'] + kelvin = lifx_features(self.bulb)['min_kelvin'] return math.ceil(color_util.color_temperature_kelvin_to_mired(kelvin)) @property @@ -432,8 +429,8 @@ class LIFXLight(Light): """Flag supported features.""" support = SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION | SUPPORT_EFFECT - device_features = lifx_features(self.light) - if device_features['min_kelvin'] != device_features['max_kelvin']: + bulb_features = lifx_features(self.bulb) + if bulb_features['min_kelvin'] != bulb_features['max_kelvin']: support |= SUPPORT_COLOR_TEMP return support @@ -441,25 +438,25 @@ class LIFXLight(Light): @property def brightness(self): """Return the brightness of this light between 0..255.""" - return convert_16_to_8(self.light.color[2]) + return convert_16_to_8(self.bulb.color[2]) @property def color_temp(self): """Return the color temperature.""" - _, sat, _, kelvin = self.light.color + _, sat, _, kelvin = self.bulb.color if sat: return None return color_util.color_temperature_kelvin_to_mired(kelvin) @property def is_on(self): - """Return true if device is on.""" - return self.light.power_level != 0 + """Return true if light is on.""" + return self.bulb.power_level != 0 @property def effect(self): """Return the name of the currently running effect.""" - effect = self.effects_conductor.effect(self.light) + effect = self.effects_conductor.effect(self.bulb) if effect: return 'lifx_effect_' + effect.name return None @@ -485,19 +482,19 @@ class LIFXLight(Light): util.dt.utcnow() + timedelta(milliseconds=when)) async def async_turn_on(self, **kwargs): - """Turn the device on.""" + """Turn the light on.""" kwargs[ATTR_POWER] = True self.hass.async_add_job(self.set_state(**kwargs)) async def async_turn_off(self, **kwargs): - """Turn the device off.""" + """Turn the light off.""" kwargs[ATTR_POWER] = False self.hass.async_add_job(self.set_state(**kwargs)) async def set_state(self, **kwargs): """Set a color on the light and turn it on/off.""" async with self.lock: - bulb = self.light + bulb = self.bulb await self.effects_conductor.stop([bulb]) @@ -544,13 +541,13 @@ class LIFXLight(Light): await self.update_during_transition(fade) async def set_power(self, ack, pwr, duration=0): - """Send a power change to the device.""" - await ack(partial(self.light.set_power, pwr, duration=duration)) + """Send a power change to the bulb.""" + await ack(partial(self.bulb.set_power, pwr, duration=duration)) async def set_color(self, ack, hsbk, kwargs, duration=0): - """Send a color change to the device.""" - hsbk = merge_hsbk(self.light.color, hsbk) - await ack(partial(self.light.set_color, hsbk, duration=duration)) + """Send a color change to the bulb.""" + hsbk = merge_hsbk(self.bulb.color, hsbk) + await ack(partial(self.bulb.set_color, hsbk, duration=duration)) async def default_effect(self, **kwargs): """Start an effect with default parameters.""" @@ -563,7 +560,7 @@ class LIFXLight(Light): async def async_update(self): """Update bulb status.""" if self.available and not self.lock.locked(): - await AwaitAioLIFX().wait(self.light.get_color) + await AwaitAioLIFX().wait(self.bulb.get_color) class LIFXWhite(LIFXLight): @@ -600,7 +597,7 @@ class LIFXColor(LIFXLight): @property def hs_color(self): """Return the hs value.""" - hue, sat, _, _ = self.light.color + hue, sat, _, _ = self.bulb.color hue = hue / 65535 * 360 sat = sat / 65535 * 100 return (hue, sat) if sat else None @@ -610,8 +607,8 @@ class LIFXStrip(LIFXColor): """Representation of a LIFX light strip with multiple zones.""" async def set_color(self, ack, hsbk, kwargs, duration=0): - """Send a color change to the device.""" - bulb = self.light + """Send a color change to the bulb.""" + bulb = self.bulb num_zones = len(bulb.color_zones) zones = kwargs.get(ATTR_ZONES) @@ -659,7 +656,7 @@ class LIFXStrip(LIFXColor): while self.available and zone < top: # Each get_color_zones can update 8 zones at once resp = await AwaitAioLIFX().wait(partial( - self.light.get_color_zones, + self.bulb.get_color_zones, start_index=zone)) if resp: zone += 8 From 0e076fb9e7a0bcbf5a0c453f2696f571c2427307 Mon Sep 17 00:00:00 2001 From: Malte Franken Date: Fri, 31 Aug 2018 20:54:25 +1000 Subject: [PATCH 004/357] avoid error in debug log mode and rss entry without title (#16316) --- homeassistant/components/feedreader.py | 4 ++-- tests/components/test_feedreader.py | 6 +++--- tests/fixtures/feedreader3.xml | 5 +++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/feedreader.py b/homeassistant/components/feedreader.py index 782fd8ac8dd..7882cdc5a15 100644 --- a/homeassistant/components/feedreader.py +++ b/homeassistant/components/feedreader.py @@ -143,7 +143,7 @@ class FeedManager: else: self._has_published_parsed = False _LOGGER.debug("No published_parsed info available for entry %s", - entry.title) + entry) entry.update({'feed_url': self._url}) self._hass.bus.fire(self._event_type, entry) @@ -164,7 +164,7 @@ class FeedManager: self._update_and_fire_entry(entry) new_entries = True else: - _LOGGER.debug("Entry %s already processed", entry.title) + _LOGGER.debug("Entry %s already processed", entry) if not new_entries: self._log_no_entries() self._firstrun = False diff --git a/tests/components/test_feedreader.py b/tests/components/test_feedreader.py index dd98ebaf189..668f116362c 100644 --- a/tests/components/test_feedreader.py +++ b/tests/components/test_feedreader.py @@ -160,11 +160,11 @@ class TestFeedreaderComponent(unittest.TestCase): manager, events = self.setup_manager(feed_data, max_entries=5) assert len(events) == 5 - def test_feed_without_publication_date(self): - """Test simple feed with entry without publication date.""" + def test_feed_without_publication_date_and_title(self): + """Test simple feed with entry without publication date and title.""" feed_data = load_fixture('feedreader3.xml') manager, events = self.setup_manager(feed_data) - assert len(events) == 2 + assert len(events) == 3 def test_feed_invalid_data(self): """Test feed with invalid data.""" diff --git a/tests/fixtures/feedreader3.xml b/tests/fixtures/feedreader3.xml index 7b28e067cfe..d8ccd119306 100644 --- a/tests/fixtures/feedreader3.xml +++ b/tests/fixtures/feedreader3.xml @@ -21,6 +21,11 @@ http://www.example.com/link/2 GUID 2 + + Description 3 + http://www.example.com/link/3 + GUID 3 + From bc618af193afcbbfbd317174a6baa032cbb2e97f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 31 Aug 2018 13:17:24 +0200 Subject: [PATCH 005/357] Bumped version to 0.77.2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 59d2d553a7b..fac257fbaed 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 77 -PATCH_VERSION = '1' +PATCH_VERSION = '2' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From d705375a9ae6195fcae8a6b44bd25c2449f86ce2 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 31 Aug 2018 13:23:22 +0200 Subject: [PATCH 006/357] Update translations --- .../components/auth/.translations/ar.json | 7 +++++ .../components/auth/.translations/de.json | 16 ++++++++++ .../components/auth/.translations/es-419.json | 12 +++++++ .../components/auth/.translations/fr.json | 16 ++++++++++ .../components/auth/.translations/it.json | 13 ++++++++ .../components/auth/.translations/ko.json | 4 +-- .../components/auth/.translations/nl.json | 16 ++++++++++ .../components/auth/.translations/no.json | 16 ++++++++++ .../components/auth/.translations/pl.json | 16 ++++++++++ .../components/auth/.translations/pt.json | 16 ++++++++++ .../components/auth/.translations/ru.json | 2 +- .../components/auth/.translations/sl.json | 16 ++++++++++ .../components/cast/.translations/fr.json | 13 ++++++++ .../components/deconz/.translations/nl.json | 2 +- .../components/hangouts/.translations/de.json | 1 + .../components/hangouts/.translations/en.json | 4 +-- .../hangouts/.translations/es-419.json | 18 +++++++++++ .../components/hangouts/.translations/fr.json | 21 +++++++++++++ .../components/hangouts/.translations/it.json | 24 ++++++++++++++ .../components/hangouts/.translations/nl.json | 29 +++++++++++++++++ .../components/hangouts/.translations/no.json | 18 ++++++++++- .../hangouts/.translations/pt-BR.json | 11 +------ .../components/hangouts/.translations/pt.json | 31 +++++++++++++++++++ .../.translations/es-419.json | 4 ++- .../homematicip_cloud/.translations/fr.json | 21 +++++++++++++ .../homematicip_cloud/.translations/it.json | 8 +++++ .../homematicip_cloud/.translations/nl.json | 8 ++--- .../homematicip_cloud/.translations/pt.json | 1 + .../components/hue/.translations/nl.json | 2 +- .../components/nest/.translations/fr.json | 7 +++++ .../sensor/.translations/moon.pt-BR.json | 2 -- .../sensor/.translations/moon.pt.json | 12 +++++++ .../components/sonos/.translations/de.json | 2 +- 33 files changed, 363 insertions(+), 26 deletions(-) create mode 100644 homeassistant/components/auth/.translations/ar.json create mode 100644 homeassistant/components/auth/.translations/de.json create mode 100644 homeassistant/components/auth/.translations/es-419.json create mode 100644 homeassistant/components/auth/.translations/fr.json create mode 100644 homeassistant/components/auth/.translations/it.json create mode 100644 homeassistant/components/auth/.translations/nl.json create mode 100644 homeassistant/components/auth/.translations/no.json create mode 100644 homeassistant/components/auth/.translations/pl.json create mode 100644 homeassistant/components/auth/.translations/pt.json create mode 100644 homeassistant/components/auth/.translations/sl.json create mode 100644 homeassistant/components/cast/.translations/fr.json create mode 100644 homeassistant/components/hangouts/.translations/es-419.json create mode 100644 homeassistant/components/hangouts/.translations/fr.json create mode 100644 homeassistant/components/hangouts/.translations/nl.json create mode 100644 homeassistant/components/hangouts/.translations/pt.json create mode 100644 homeassistant/components/homematicip_cloud/.translations/fr.json create mode 100644 homeassistant/components/nest/.translations/fr.json create mode 100644 homeassistant/components/sensor/.translations/moon.pt.json diff --git a/homeassistant/components/auth/.translations/ar.json b/homeassistant/components/auth/.translations/ar.json new file mode 100644 index 00000000000..1ef902e6fe2 --- /dev/null +++ b/homeassistant/components/auth/.translations/ar.json @@ -0,0 +1,7 @@ +{ + "mfa_setup": { + "totp": { + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/de.json b/homeassistant/components/auth/.translations/de.json new file mode 100644 index 00000000000..67f948e8340 --- /dev/null +++ b/homeassistant/components/auth/.translations/de.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "Ung\u00fcltiger Code, bitte versuche es erneut. Wenn Sie diesen Fehler regelm\u00e4\u00dfig erhalten, stelle sicher, dass die Uhr deines Home Assistant-Systems korrekt ist." + }, + "step": { + "init": { + "description": "Um die Zwei-Faktor-Authentifizierung mit zeitbasierten Einmalpassw\u00f6rtern zu aktivieren, scanne den QR-Code mit Ihrer Authentifizierungs-App. Wenn du keine hast, empfehlen wir entweder [Google Authenticator] (https://support.google.com/accounts/answer/1066447) oder [Authy] (https://authy.com/). \n\n {qr_code} \n \nNachdem du den Code gescannt hast, gebe den sechsstelligen Code aus der App ein, um das Setup zu \u00fcberpr\u00fcfen. Wenn es Probleme beim Scannen des QR-Codes gibt, f\u00fchre ein manuelles Setup mit dem Code ** ` {code} ` ** durch.", + "title": "Richte die Zwei-Faktor-Authentifizierung mit TOTP ein" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/es-419.json b/homeassistant/components/auth/.translations/es-419.json new file mode 100644 index 00000000000..6caa9d49993 --- /dev/null +++ b/homeassistant/components/auth/.translations/es-419.json @@ -0,0 +1,12 @@ +{ + "mfa_setup": { + "totp": { + "step": { + "init": { + "title": "Configurar la autenticaci\u00f3n de dos factores mediante TOTP" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/fr.json b/homeassistant/components/auth/.translations/fr.json new file mode 100644 index 00000000000..e8a8037c39a --- /dev/null +++ b/homeassistant/components/auth/.translations/fr.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "Code invalide. S'il vous pla\u00eet essayez \u00e0 nouveau. Si cette erreur persiste, assurez-vous que l'horloge de votre syst\u00e8me Home Assistant est correcte." + }, + "step": { + "init": { + "description": "Pour activer l'authentification \u00e0 deux facteurs \u00e0 l'aide de mots de passe \u00e0 utilisation unique bas\u00e9s sur l'heure, num\u00e9risez le code QR avec votre application d'authentification. Si vous n'en avez pas, nous vous recommandons d'utiliser [Google Authenticator] (https://support.google.com/accounts/answer/1066447) ou [Authy] (https://authy.com/). \n\n {qr_code} \n \n Apr\u00e8s avoir num\u00e9ris\u00e9 le code, entrez le code \u00e0 six chiffres de votre application pour v\u00e9rifier la configuration. Si vous rencontrez des probl\u00e8mes lors de l\u2019analyse du code QR, effectuez une configuration manuelle avec le code ** ` {code} ` **.", + "title": "Configurer une authentification \u00e0 deux facteurs \u00e0 l'aide de TOTP" + } + }, + "title": "TOTP (Mot de passe \u00e0 utilisation unique bas\u00e9 sur le temps)" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/it.json b/homeassistant/components/auth/.translations/it.json new file mode 100644 index 00000000000..869c3b438af --- /dev/null +++ b/homeassistant/components/auth/.translations/it.json @@ -0,0 +1,13 @@ +{ + "mfa_setup": { + "totp": { + "step": { + "init": { + "description": "Per attivare l'autenticazione a due fattori utilizzando password monouso basate sul tempo, eseguire 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 \n Dopo aver scansionato il codice, 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 codice ** ` {code} ` **.", + "title": "Imposta l'autenticazione a due fattori usando TOTP" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/ko.json b/homeassistant/components/auth/.translations/ko.json index 726fa6a6cd1..4eb4783edd9 100644 --- a/homeassistant/components/auth/.translations/ko.json +++ b/homeassistant/components/auth/.translations/ko.json @@ -2,11 +2,11 @@ "mfa_setup": { "totp": { "error": { - "invalid_code": "\uc798\ubabb\ub41c \ucf54\ub4dc \uc785\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694. \uc774 \uc624\ub958\uac00 \uc9c0\uc18d\uc801\uc73c\ub85c \ubc1c\uc0dd\ud55c\ub2e4\uba74 Home Assistant \uc758 \uc2dc\uacc4\uac00 \uc815\ud655\ud55c\uc9c0 \ud655\uc778\ud574\ubcf4\uc138\uc694." + "invalid_code": "\uc798\ubabb\ub41c \ucf54\ub4dc \uc785\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694. \uc774 \uc624\ub958\uac00 \uc9c0\uc18d\uc801\uc73c\ub85c \ubc1c\uc0dd\ud55c\ub2e4\uba74 Home Assistant \uc758 \uc2dc\uac04\uc124\uc815\uc774 \uc62c\ubc14\ub978\uc9c0 \ud655\uc778\ud574\ubcf4\uc138\uc694." }, "step": { "init": { - "description": "\uc2dc\uac04 \uae30\ubc18\uc758 \uc77c\ud68c\uc6a9 \ube44\ubc00\ubc88\ud638\ub97c \uc0ac\uc6a9\ud558\ub294 2\ub2e8\uacc4 \uc778\uc99d\uc744 \ud558\ub824\uba74 \uc778\uc99d\uc6a9 \uc571\uc744 \uc774\uc6a9\ud574\uc11c QR \ucf54\ub4dc\ub97c \uc2a4\uce94\ud574 \uc8fc\uc138\uc694. \uc778\uc99d\uc6a9 \uc571\uc740 [Google Authenticator](https://support.google.com/accounts/answer/1066447) \ub098 [Authy](https://authy.com/) \ub97c \ucd94\ucc9c\ub4dc\ub9bd\ub2c8\ub2e4.\n\n{qr_code}\n\n\uc2a4\uce94 \ud6c4\uc5d0 \uc0dd\uc131\ub41c 6\uc790\ub9ac \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc11c \uc124\uc815\uc744 \ud655\uc778\ud558\uc138\uc694. QR \ucf54\ub4dc \uc2a4\uce94\uc5d0 \ubb38\uc81c\uac00 \uc788\ub2e4\uba74, **`{code}`** \ucf54\ub4dc\ub85c \uc9c1\uc811 \uc124\uc815\ud574\ubcf4\uc138\uc694.", + "description": "\uc2dc\uac04 \uae30\ubc18\uc758 \uc77c\ud68c\uc6a9 \ube44\ubc00\ubc88\ud638\ub97c \uc0ac\uc6a9\ud558\ub294 2\ub2e8\uacc4 \uc778\uc99d\uc744 \ud558\ub824\uba74 \uc778\uc99d\uc6a9 \uc571\uc744 \uc774\uc6a9\ud574\uc11c QR \ucf54\ub4dc\ub97c \uc2a4\uce94\ud574 \uc8fc\uc138\uc694. \uc778\uc99d\uc6a9 \uc571\uc740 [Google Authenticator](https://support.google.com/accounts/answer/1066447) \ub610\ub294 [Authy](https://authy.com/) \ub97c \ucd94\ucc9c\ub4dc\ub9bd\ub2c8\ub2e4.\n\n{qr_code}\n\n\uc2a4\uce94 \ud6c4\uc5d0 \uc0dd\uc131\ub41c 6\uc790\ub9ac \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc11c \uc124\uc815\uc744 \ud655\uc778\ud558\uc138\uc694. QR \ucf54\ub4dc \uc2a4\uce94\uc5d0 \ubb38\uc81c\uac00 \uc788\ub2e4\uba74, **`{code}`** \ucf54\ub4dc\ub85c \uc9c1\uc811 \uc124\uc815\ud574\ubcf4\uc138\uc694.", "title": "TOTP \ub97c \uc0ac\uc6a9\ud558\uc5ec 2 \ub2e8\uacc4 \uc778\uc99d \uad6c\uc131" } }, diff --git a/homeassistant/components/auth/.translations/nl.json b/homeassistant/components/auth/.translations/nl.json new file mode 100644 index 00000000000..40a873023dd --- /dev/null +++ b/homeassistant/components/auth/.translations/nl.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "Ongeldige code, probeer het opnieuw. Als u deze fout blijft krijgen, controleer dan of de klok van uw Home Assistant systeem correct is ingesteld." + }, + "step": { + "init": { + "description": "Voor het activeren van twee-factor-authenticatie via tijdgebonden eenmalige wachtwoorden: scan de QR code met uw authenticatie-app. Als u nog geen app heeft, adviseren we [Google Authenticator (https://support.google.com/accounts/answer/1066447) of [Authy](https://authy.com/).\n\n{qr_code}\n\nNa het scannen van de code voert u de zescijferige code uit uw app in om de instelling te controleren. Als u problemen heeft met het scannen van de QR-code, voert u een handmatige configuratie uit met code **`{code}`**.", + "title": "Configureer twee-factor-authenticatie via TOTP" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/no.json b/homeassistant/components/auth/.translations/no.json new file mode 100644 index 00000000000..43ec497cfb1 --- /dev/null +++ b/homeassistant/components/auth/.translations/no.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "Ugyldig kode, pr\u00f8v igjen. Hvis du f\u00e5r denne feilen konsekvent, m\u00e5 du s\u00f8rge for at klokken p\u00e5 Home Assistant systemet er riktig." + }, + "step": { + "init": { + "description": "For \u00e5 aktivere tofaktorautentisering ved hjelp av tidsbaserte engangspassord, skann QR-koden med autentiseringsappen din. Hvis du ikke har en, kan vi anbefale enten [Google Authenticator](https://support.google.com/accounts/answer/1066447) eller [Authy](https://authy.com/). \n\n {qr_code} \n \nEtter at du har skannet koden, skriver du inn den seks-sifrede koden fra appen din for \u00e5 kontrollere oppsettet. Dersom du har problemer med \u00e5 skanne QR-koden kan du taste inn f\u00f8lgende kode manuelt: **`{code}`**.", + "title": "Konfigurer tofaktorautentisering ved hjelp av TOTP" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/pl.json b/homeassistant/components/auth/.translations/pl.json new file mode 100644 index 00000000000..78999c34c22 --- /dev/null +++ b/homeassistant/components/auth/.translations/pl.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "Nieprawid\u0142owy kod, spr\u00f3buj ponownie. Je\u015bli b\u0142\u0105d b\u0119dzie si\u0119 powtarza\u0142, upewnij si\u0119, \u017ce czas zegara systemu Home Assistant jest prawid\u0142owy." + }, + "step": { + "init": { + "description": "Aby aktywowa\u0107 uwierzytelnianie dwusk\u0142adnikowe przy u\u017cyciu jednorazowych hase\u0142 opartych na czasie, zeskanuj kod QR za pomoc\u0105 aplikacji uwierzytelniaj\u0105cej. Je\u015bli jej nie masz, polecamy [Google Authenticator](https://support.google.com/accounts/answer/1066447) lub [Authy](https://authy.com/).\n\n{qr_code} \n \nPo zeskanowaniu kodu wprowad\u017a sze\u015bciocyfrowy kod z aplikacji, aby zweryfikowa\u0107 konfiguracj\u0119. Je\u015bli masz problemy z zeskanowaniem kodu QR, wykonaj r\u0119czn\u0105 konfiguracj\u0119 z kodem **`{code}`**.", + "title": "Skonfiguruj uwierzytelnianie dwusk\u0142adnikowe za pomoc\u0105 hase\u0142 jednorazowych opartych na czasie" + } + }, + "title": "Has\u0142a jednorazowe oparte na czasie" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/pt.json b/homeassistant/components/auth/.translations/pt.json new file mode 100644 index 00000000000..474dbe488be --- /dev/null +++ b/homeassistant/components/auth/.translations/pt.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "C\u00f3digo inv\u00e1lido, por favor, tente novamente. Se receber este erro constantemente, por favor, certifique-se de que o rel\u00f3gio do sistema que hospeda o Home Assistent \u00e9 preciso." + }, + "step": { + "init": { + "description": "Para ativar a autentica\u00e7\u00e3o com dois fatores utilizando passwords unicas temporais (OTP), ler o c\u00f3digo QR com a sua aplica\u00e7\u00e3o de autentica\u00e7\u00e3o. Se voc\u00ea n\u00e3o tiver uma, recomendamos [Google Authenticator](https://support.google.com/accounts/answer/1066447) ou [Authy](https://authy.com/).\n\n{qr_code}\n\nDepois de ler o c\u00f3digo, introduza o c\u00f3digo de seis d\u00edgitos fornecido pela sua aplica\u00e7\u00e3o para verificar a configura\u00e7\u00e3o. Se tiver problemas a ler o c\u00f3digo QR, fa\u00e7a uma configura\u00e7\u00e3o manual com o c\u00f3digo **`{c\u00f3digo}`**.", + "title": "Configurar autentica\u00e7\u00e3o com dois fatores usando TOTP" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/ru.json b/homeassistant/components/auth/.translations/ru.json index b4b5b58f9fa..a716425f345 100644 --- a/homeassistant/components/auth/.translations/ru.json +++ b/homeassistant/components/auth/.translations/ru.json @@ -6,7 +6,7 @@ }, "step": { "init": { - "description": "\u0427\u0442\u043e\u0431\u044b \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0443\u044e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0445 \u043f\u0430\u0440\u043e\u043b\u0435\u0439, \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043e\u0442\u0441\u043a\u0430\u043d\u0438\u0440\u0443\u0439\u0442\u0435 QR-\u043a\u043e\u0434 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0433\u043e \u043d\u0435\u0442, \u043c\u044b \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u0438\u0431\u043e [Google Authenticator] (https://support.google.com/accounts/answer/1066447), \u043b\u0438\u0431\u043e [Authy] (https://authy.com/). \n\n {qr_code} \n \n \u041f\u043e\u0441\u043b\u0435 \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f QR-\u043a\u043e\u0434\u0430 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0448\u0435\u0441\u0442\u0438\u0437\u043d\u0430\u0447\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438\u0437 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441\u043e \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c QR-\u043a\u043e\u0434\u0430, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0440\u0443\u0447\u043d\u0443\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0441 \u043a\u043e\u0434\u043e\u043c ** ` {code} ` **.", + "description": "\u0427\u0442\u043e\u0431\u044b \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0443\u044e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0445 \u043f\u0430\u0440\u043e\u043b\u0435\u0439, \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043e\u0442\u0441\u043a\u0430\u043d\u0438\u0440\u0443\u0439\u0442\u0435 QR-\u043a\u043e\u0434 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0433\u043e \u043d\u0435\u0442, \u043c\u044b \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u0438\u0431\u043e [Google Authenticator](https://support.google.com/accounts/answer/1066447), \u043b\u0438\u0431\u043e [Authy](https://authy.com/). \n\n {qr_code} \n \n\u041f\u043e\u0441\u043b\u0435 \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f QR-\u043a\u043e\u0434\u0430 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0448\u0435\u0441\u0442\u0438\u0437\u043d\u0430\u0447\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438\u0437 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441\u043e \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c QR-\u043a\u043e\u0434\u0430, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0434\u0430 **`{code}`**.", "title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \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 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c TOTP" } }, diff --git a/homeassistant/components/auth/.translations/sl.json b/homeassistant/components/auth/.translations/sl.json new file mode 100644 index 00000000000..45b57a772f9 --- /dev/null +++ b/homeassistant/components/auth/.translations/sl.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "Neveljavna koda, prosimo, poskusite znova. \u010ce dobite to sporo\u010dilo ve\u010dkrat, prosimo poskrbite, da bo ura va\u0161ega Home Assistenta to\u010dna." + }, + "step": { + "init": { + "description": "\u010ce \u017eelite aktivirati preverjanje pristnosti dveh faktorjev z enkratnimi gesli, ki temeljijo na \u010dasu, skenirajte kodo QR s svojo aplikacijo za preverjanje pristnosti. \u010ce je nimate, priporo\u010damo bodisi [Google Authenticator] (https://support.google.com/accounts/answer/1066447) ali [Authy] (https://authy.com/). \n\n {qr_code} \n \n Po skeniranju kode vnesite \u0161estmestno kodo iz aplikacije, da preverite nastavitev. \u010ce imate te\u017eave pri skeniranju kode QR, naredite ro\u010dno nastavitev s kodo ** ` {code} ` **.", + "title": "Nastavite dvofaktorsko avtentifikacijo s pomo\u010djo TOTP-ja" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/cast/.translations/fr.json b/homeassistant/components/cast/.translations/fr.json new file mode 100644 index 00000000000..acacddf2187 --- /dev/null +++ b/homeassistant/components/cast/.translations/fr.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "no_devices_found": "Aucun appareil Google Cast trouv\u00e9 sur le r\u00e9seau.", + "single_instance_allowed": "Seulement une seule configuration de Google Cast est n\u00e9cessaire." + }, + "step": { + "confirm": { + "description": "Voulez-vous configurer Google Cast?" + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/deconz/.translations/nl.json b/homeassistant/components/deconz/.translations/nl.json index 6f3fa2ec9a4..9084d22f4a3 100644 --- a/homeassistant/components/deconz/.translations/nl.json +++ b/homeassistant/components/deconz/.translations/nl.json @@ -28,6 +28,6 @@ "title": "Extra configuratieopties voor deCONZ" } }, - "title": "deCONZ" + "title": "deCONZ Zigbee gateway" } } \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/de.json b/homeassistant/components/hangouts/.translations/de.json index 4222e7f5556..a2ed8d21230 100644 --- a/homeassistant/components/hangouts/.translations/de.json +++ b/homeassistant/components/hangouts/.translations/de.json @@ -5,6 +5,7 @@ "unknown": "Ein unbekannter Fehler ist aufgetreten." }, "error": { + "invalid_2fa": "Ung\u00fcltige 2-Faktor Authentifizierung, bitte versuche es erneut.", "invalid_2fa_method": "Ung\u00fcltige 2FA Methode (mit Telefon verifizieren)", "invalid_login": "Ung\u00fcltige Daten, bitte erneut versuchen." }, diff --git a/homeassistant/components/hangouts/.translations/en.json b/homeassistant/components/hangouts/.translations/en.json index 6e70a1f4310..f526bec4f34 100644 --- a/homeassistant/components/hangouts/.translations/en.json +++ b/homeassistant/components/hangouts/.translations/en.json @@ -5,7 +5,7 @@ "unknown": "Unknown error occurred." }, "error": { - "invalid_2fa": "Invalid 2 Factor Authorization, please try again.", + "invalid_2fa": "Invalid 2 Factor Authentication, please try again.", "invalid_2fa_method": "Invalid 2FA Method (Verify on Phone).", "invalid_login": "Invalid Login, please try again." }, @@ -14,7 +14,7 @@ "data": { "2fa": "2FA Pin" }, - "title": "2-Factor-Authorization" + "title": "2-Factor-Authentication" }, "user": { "data": { diff --git a/homeassistant/components/hangouts/.translations/es-419.json b/homeassistant/components/hangouts/.translations/es-419.json new file mode 100644 index 00000000000..a3699db08ae --- /dev/null +++ b/homeassistant/components/hangouts/.translations/es-419.json @@ -0,0 +1,18 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts ya est\u00e1 configurado", + "unknown": "Se produjo un error desconocido." + }, + "step": { + "user": { + "data": { + "email": "Direcci\u00f3n de correo electr\u00f3nico", + "password": "Contrase\u00f1a" + }, + "title": "Inicio de sesi\u00f3n de Google Hangouts" + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/fr.json b/homeassistant/components/hangouts/.translations/fr.json new file mode 100644 index 00000000000..c92d478c454 --- /dev/null +++ b/homeassistant/components/hangouts/.translations/fr.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts est d\u00e9j\u00e0 configur\u00e9", + "unknown": "Une erreur inconnue s'est produite" + }, + "error": { + "invalid_login": "Login invalide, veuillez r\u00e9essayer." + }, + "step": { + "2fa": { + "title": "Authentification \u00e0 2 facteurs" + }, + "user": { + "data": { + "password": "Mot de passe" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/it.json b/homeassistant/components/hangouts/.translations/it.json index 0c609b3430a..76a9adcb40e 100644 --- a/homeassistant/components/hangouts/.translations/it.json +++ b/homeassistant/components/hangouts/.translations/it.json @@ -1,5 +1,29 @@ { "config": { + "abort": { + "already_configured": "Google Hangouts \u00e8 gi\u00e0 configurato", + "unknown": "Si \u00e8 verificato un errore sconosciuto." + }, + "error": { + "invalid_2fa": "Autenticazione a 2 fattori non valida, riprovare.", + "invalid_2fa_method": "Metodo 2FA non valido (verifica sul telefono).", + "invalid_login": "Accesso non valido, si prega di riprovare." + }, + "step": { + "2fa": { + "data": { + "2fa": "2FA Pin" + }, + "title": "Autenticazione a due fattori" + }, + "user": { + "data": { + "email": "Indirizzo email", + "password": "Password" + }, + "title": "Accesso a Google Hangouts" + } + }, "title": "Google Hangouts" } } \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/nl.json b/homeassistant/components/hangouts/.translations/nl.json new file mode 100644 index 00000000000..cf73210aa3b --- /dev/null +++ b/homeassistant/components/hangouts/.translations/nl.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts is al geconfigureerd", + "unknown": "Onbekende fout opgetreden." + }, + "error": { + "invalid_2fa": "Ongeldige twee-factor-authenticatie, probeer het opnieuw.", + "invalid_2fa_method": "Ongeldige 2FA-methode (verifi\u00ebren op telefoon).", + "invalid_login": "Ongeldige aanmelding, probeer het opnieuw." + }, + "step": { + "2fa": { + "data": { + "2fa": "2FA pin" + }, + "title": "Twee-factor-authenticatie" + }, + "user": { + "data": { + "email": "E-mailadres", + "password": "Wachtwoord" + }, + "title": "Google Hangouts inlog" + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/no.json b/homeassistant/components/hangouts/.translations/no.json index 7ea074470c7..c2cdb93c005 100644 --- a/homeassistant/components/hangouts/.translations/no.json +++ b/homeassistant/components/hangouts/.translations/no.json @@ -1,11 +1,27 @@ { "config": { + "abort": { + "already_configured": "Google Hangouts er allerede konfigurert", + "unknown": "Ukjent feil oppstod." + }, + "error": { + "invalid_2fa": "Ugyldig tofaktorautentisering, vennligst pr\u00f8v igjen.", + "invalid_2fa_method": "Ugyldig 2FA-metode (Bekreft p\u00e5 telefon).", + "invalid_login": "Ugyldig innlogging, vennligst pr\u00f8v igjen." + }, "step": { + "2fa": { + "data": { + "2fa": "2FA Pin" + }, + "title": "Tofaktorautentisering" + }, "user": { "data": { "email": "E-postadresse", "password": "Passord" - } + }, + "title": "Google Hangouts p\u00e5logging" } }, "title": "Google Hangouts" diff --git a/homeassistant/components/hangouts/.translations/pt-BR.json b/homeassistant/components/hangouts/.translations/pt-BR.json index 4dffe492c4d..41b097f3f8d 100644 --- a/homeassistant/components/hangouts/.translations/pt-BR.json +++ b/homeassistant/components/hangouts/.translations/pt-BR.json @@ -1,23 +1,14 @@ { "config": { "abort": { - "already_configured": "Hangouts do Google j\u00e1 est\u00e1 configurado.", - "unknown": "Ocorreu um erro desconhecido." - }, - "error": { - "invalid_2fa_method": "M\u00e9todo 2FA inv\u00e1lido (verificar no telefone).", - "invalid_login": "Login inv\u00e1lido, por favor, tente novamente." + "already_configured": "Hangouts do Google j\u00e1 est\u00e1 configurado." }, "step": { "2fa": { - "data": { - "2fa": "Pin 2FA" - }, "title": "" }, "user": { "data": { - "email": "Endere\u00e7o de e-mail", "password": "Senha" }, "title": "Login do Hangouts do Google" diff --git a/homeassistant/components/hangouts/.translations/pt.json b/homeassistant/components/hangouts/.translations/pt.json new file mode 100644 index 00000000000..64c960a121a --- /dev/null +++ b/homeassistant/components/hangouts/.translations/pt.json @@ -0,0 +1,31 @@ +{ + "config": { + "abort": { + "already_configured": "Google Hangouts j\u00e1 est\u00e1 configurado", + "unknown": "Ocorreu um erro desconhecido." + }, + "error": { + "invalid_2fa": "Autoriza\u00e7\u00e3o por 2 factores inv\u00e1lida, por favor, tente novamente.", + "invalid_2fa_method": "M\u00e9todo 2FA inv\u00e1lido (verificar no telefone).", + "invalid_login": "Login inv\u00e1lido, por favor, tente novamente." + }, + "step": { + "2fa": { + "data": { + "2fa": "Pin 2FA" + }, + "description": "Vazio", + "title": "" + }, + "user": { + "data": { + "email": "Endere\u00e7o de e-mail", + "password": "Palavra-passe" + }, + "description": "Vazio", + "title": "Login Google Hangouts" + } + }, + "title": "" + } +} \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/.translations/es-419.json b/homeassistant/components/homematicip_cloud/.translations/es-419.json index 9af47289380..e15d0dbae64 100644 --- a/homeassistant/components/homematicip_cloud/.translations/es-419.json +++ b/homeassistant/components/homematicip_cloud/.translations/es-419.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "Accesspoint ya est\u00e1 configurado", "conection_aborted": "No se pudo conectar al servidor HMIP", + "connection_aborted": "No se pudo conectar al servidor HMIP", "unknown": "Se produjo un error desconocido." }, "error": { @@ -18,6 +19,7 @@ "pin": "C\u00f3digo PIN (opcional)" } } - } + }, + "title": "HomematicIP Cloud" } } \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/.translations/fr.json b/homeassistant/components/homematicip_cloud/.translations/fr.json new file mode 100644 index 00000000000..c10cb519133 --- /dev/null +++ b/homeassistant/components/homematicip_cloud/.translations/fr.json @@ -0,0 +1,21 @@ +{ + "config": { + "abort": { + "unknown": "Une erreur inconnue s'est produite" + }, + "error": { + "invalid_pin": "Code PIN invalide, veuillez r\u00e9essayer.", + "press_the_button": "Veuillez appuyer sur le bouton bleu.", + "register_failed": "\u00c9chec d'enregistrement. Veuillez r\u00e9essayer." + }, + "step": { + "init": { + "data": { + "hapid": "ID du point d'acc\u00e8s (SGTIN)", + "name": "Nom (facultatif, utilis\u00e9 comme pr\u00e9fixe de nom pour tous les p\u00e9riph\u00e9riques)", + "pin": "Code PIN (facultatif)" + } + } + } + } +} \ No newline at end of file diff --git a/homeassistant/components/homematicip_cloud/.translations/it.json b/homeassistant/components/homematicip_cloud/.translations/it.json index 2566eb25570..95e600e6d03 100644 --- a/homeassistant/components/homematicip_cloud/.translations/it.json +++ b/homeassistant/components/homematicip_cloud/.translations/it.json @@ -1,5 +1,13 @@ { "config": { + "abort": { + "already_configured": "Il punto di accesso \u00e8 gi\u00e0 configurato", + "connection_aborted": "Impossibile connettersi al server HMIP" + }, + "error": { + "press_the_button": "Si prega di premere il pulsante blu.", + "register_failed": "Registrazione fallita, si prega di riprovare." + }, "step": { "init": { "data": { diff --git a/homeassistant/components/homematicip_cloud/.translations/nl.json b/homeassistant/components/homematicip_cloud/.translations/nl.json index 23305a7e584..40d1ced5007 100644 --- a/homeassistant/components/homematicip_cloud/.translations/nl.json +++ b/homeassistant/components/homematicip_cloud/.translations/nl.json @@ -1,7 +1,7 @@ { "config": { "abort": { - "already_configured": "Accesspoint is reeds geconfigureerd", + "already_configured": "Accesspoint is al geconfigureerd", "conection_aborted": "Kon geen verbinding maken met de HMIP-server", "connection_aborted": "Kon geen verbinding maken met de HMIP-server", "unknown": "Er is een onbekende fout opgetreden." @@ -19,11 +19,11 @@ "name": "Naam (optioneel, gebruikt als naamprefix voor alle apparaten)", "pin": "Pin-Code (optioneel)" }, - "title": "Kies HomematicIP Accesspoint" + "title": "Kies HomematicIP accesspoint" }, "link": { - "description": "Druk op de blauwe knop op de accesspoint en de verzendknop om HomematicIP met de Home Assistant te registreren. \n\n![Locatie van knop op brug](/static/images/config_flows/\nconfig_homematicip_cloud.png)", - "title": "Link Accesspoint" + "description": "Druk op de blauwe knop op het accesspoint en de verzendknop om HomematicIP bij Home Assistant te registreren. \n\n![Locatie van knop op bridge](/static/images/config_flows/\nconfig_homematicip_cloud.png)", + "title": "Link accesspoint" } }, "title": "HomematicIP Cloud" diff --git a/homeassistant/components/homematicip_cloud/.translations/pt.json b/homeassistant/components/homematicip_cloud/.translations/pt.json index 2266e83ac44..87ee494a875 100644 --- a/homeassistant/components/homematicip_cloud/.translations/pt.json +++ b/homeassistant/components/homematicip_cloud/.translations/pt.json @@ -3,6 +3,7 @@ "abort": { "already_configured": "O ponto de acesso j\u00e1 se encontra configurado", "conection_aborted": "N\u00e3o foi poss\u00edvel ligar ao servidor HMIP", + "connection_aborted": "N\u00e3o foi poss\u00edvel ligar ao servidor HMIP", "unknown": "Ocorreu um erro desconhecido." }, "error": { diff --git a/homeassistant/components/hue/.translations/nl.json b/homeassistant/components/hue/.translations/nl.json index 88c611b1633..bd065bb7506 100644 --- a/homeassistant/components/hue/.translations/nl.json +++ b/homeassistant/components/hue/.translations/nl.json @@ -24,6 +24,6 @@ "title": "Link Hub" } }, - "title": "Philips Hue Bridge" + "title": "Philips Hue" } } \ No newline at end of file diff --git a/homeassistant/components/nest/.translations/fr.json b/homeassistant/components/nest/.translations/fr.json new file mode 100644 index 00000000000..62a4d7deec9 --- /dev/null +++ b/homeassistant/components/nest/.translations/fr.json @@ -0,0 +1,7 @@ +{ + "config": { + "abort": { + "already_setup": "Vous ne pouvez configurer qu'un seul compte Nest." + } + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.pt-BR.json b/homeassistant/components/sensor/.translations/moon.pt-BR.json index af4cefff6e5..57d3a3e95e4 100644 --- a/homeassistant/components/sensor/.translations/moon.pt-BR.json +++ b/homeassistant/components/sensor/.translations/moon.pt-BR.json @@ -1,8 +1,6 @@ { "state": { - "first_quarter": "Quarto crescente", "full_moon": "Cheia", - "last_quarter": "Quarto minguante", "new_moon": "Nova", "waning_crescent": "Minguante", "waning_gibbous": "Minguante gibosa", diff --git a/homeassistant/components/sensor/.translations/moon.pt.json b/homeassistant/components/sensor/.translations/moon.pt.json new file mode 100644 index 00000000000..14961ab98f0 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.pt.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Quarto crescente", + "full_moon": "Lua cheia", + "last_quarter": "Quarto minguante", + "new_moon": "Lua nova", + "waning_crescent": "Lua crescente", + "waning_gibbous": "Minguante convexa", + "waxing_crescent": "Lua minguante", + "waxing_gibbous": "Crescente convexa" + } +} \ No newline at end of file diff --git a/homeassistant/components/sonos/.translations/de.json b/homeassistant/components/sonos/.translations/de.json index d0587036d24..dd44fca5888 100644 --- a/homeassistant/components/sonos/.translations/de.json +++ b/homeassistant/components/sonos/.translations/de.json @@ -6,7 +6,7 @@ }, "step": { "confirm": { - "description": "M\u00f6chten Sie Sonos konfigurieren?", + "description": "M\u00f6chten Sie Sonos einrichten?", "title": "Sonos" } }, From 2c7d6ee6b538de16e0de4c93b0667faf4d934a33 Mon Sep 17 00:00:00 2001 From: Daniel Perna Date: Sat, 1 Sep 2018 10:40:16 +0200 Subject: [PATCH 007/357] Fix missing humidity sensor (#16337) --- homeassistant/components/homematic/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/homematic/__init__.py b/homeassistant/components/homematic/__init__.py index 53c8e267016..2b517652ad7 100644 --- a/homeassistant/components/homematic/__init__.py +++ b/homeassistant/components/homematic/__init__.py @@ -77,7 +77,7 @@ HM_DEVICE_TYPES = { 'FillingLevel', 'ValveDrive', 'EcoLogic', 'IPThermostatWall', 'IPSmoke', 'RFSiren', 'PresenceIP', 'IPAreaThermostat', 'IPWeatherSensor', 'RotaryHandleSensorIP', 'IPPassageSensor', - 'IPKeySwitchPowermeter'], + 'IPKeySwitchPowermeter', 'IPThermostatWall230V'], DISCOVER_CLIMATE: [ 'Thermostat', 'ThermostatWall', 'MAXThermostat', 'ThermostatWall2', 'MAXWallThermostat', 'IPThermostat', 'IPThermostatWall', From 901cfef78e5961cf88055b2d01be39773df567d2 Mon Sep 17 00:00:00 2001 From: Philipp Temminghoff Date: Sat, 1 Sep 2018 16:02:38 +0200 Subject: [PATCH 008/357] Support Sonos Beam HDMI input (#16340) --- homeassistant/components/media_player/sonos.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/media_player/sonos.py b/homeassistant/components/media_player/sonos.py index 4fc6b8b0954..456252aded4 100644 --- a/homeassistant/components/media_player/sonos.py +++ b/homeassistant/components/media_player/sonos.py @@ -884,6 +884,8 @@ class SonosDevice(MediaPlayerDevice): sources += [SOURCE_LINEIN] elif 'PLAYBAR' in model: sources += [SOURCE_LINEIN, SOURCE_TV] + elif 'BEAM' in model: + sources += [SOURCE_TV] return sources From a5d95dfbdcf0d9ca3da93f44e2cbb5d162ac3272 Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Sat, 1 Sep 2018 11:49:03 -0500 Subject: [PATCH 009/357] Make last_seen attribute a timezone aware datetime in UTC (#16348) The last_seen attribute was a datetime in the local timezone but with no tzinfo (i.e., a "naive" datetime.) When state changes occurred it would be printed incorrectly in homeassistant.log because homeassistant.util.dt.as_local assumes any datetime without tzinfo is UTC. Also most, if not all, datetime attributes are timezone aware in UTC. So use homeassistant.util.dt.as_utc (which assumes a naive datetime is local) to convert last_seen to a timezone aware datetime in UTC. --- homeassistant/components/device_tracker/google_maps.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/device_tracker/google_maps.py b/homeassistant/components/device_tracker/google_maps.py index 8c21e71bd30..170d3de6800 100644 --- a/homeassistant/components/device_tracker/google_maps.py +++ b/homeassistant/components/device_tracker/google_maps.py @@ -15,7 +15,7 @@ from homeassistant.const import ATTR_ID, CONF_PASSWORD, CONF_USERNAME import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import track_time_interval from homeassistant.helpers.typing import ConfigType -from homeassistant.util import slugify +from homeassistant.util import slugify, dt as dt_util REQUIREMENTS = ['locationsharinglib==2.0.11'] @@ -92,7 +92,7 @@ class GoogleMapsScanner: ATTR_ADDRESS: person.address, ATTR_FULL_NAME: person.full_name, ATTR_ID: person.id, - ATTR_LAST_SEEN: person.datetime, + ATTR_LAST_SEEN: dt_util.as_utc(person.datetime), ATTR_NICKNAME: person.nickname, } self.see( From b31890c4cbc18ec98ae92e3caefa5f88f6532367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sat, 1 Sep 2018 23:30:34 +0200 Subject: [PATCH 010/357] Handle netatmo exception (#16344) --- homeassistant/components/sensor/netatmo.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/netatmo.py b/homeassistant/components/sensor/netatmo.py index 5216913528a..f709e0169cf 100644 --- a/homeassistant/components/sensor/netatmo.py +++ b/homeassistant/components/sensor/netatmo.py @@ -317,7 +317,11 @@ class NetAtmoData: try: import pyatmo - self.station_data = pyatmo.WeatherStationData(self.auth) + try: + self.station_data = pyatmo.WeatherStationData(self.auth) + except TypeError: + _LOGGER.error("Failed to connect to NetAtmo") + return # finally statement will be executed if self.station is not None: self.data = self.station_data.lastData( From 444df5b09a9a853beb20b25fae6c3e7b7349f786 Mon Sep 17 00:00:00 2001 From: Joshi <42069141+Joshi425@users.noreply.github.com> Date: Sat, 1 Sep 2018 23:34:38 +0200 Subject: [PATCH 011/357] Add support for sound_mode for Yamaha rxv media_player (#16352) --- .../components/media_player/yamaha.py | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/yamaha.py b/homeassistant/components/media_player/yamaha.py index 2ffe58b02af..be6df547f1d 100644 --- a/homeassistant/components/media_player/yamaha.py +++ b/homeassistant/components/media_player/yamaha.py @@ -14,7 +14,7 @@ from homeassistant.components.media_player import ( SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK, SUPPORT_SELECT_SOURCE, SUPPORT_STOP, SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, - MediaPlayerDevice) + SUPPORT_SELECT_SOUND_MODE, MediaPlayerDevice) from homeassistant.const import ( ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, STATE_IDLE, STATE_OFF, STATE_ON, STATE_PLAYING) @@ -43,7 +43,8 @@ ENABLE_OUTPUT_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({ SERVICE_ENABLE_OUTPUT = 'yamaha_enable_output' SUPPORT_YAMAHA = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ - SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE | SUPPORT_PLAY + SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE | SUPPORT_PLAY \ + | SUPPORT_SELECT_SOUND_MODE PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, @@ -140,6 +141,8 @@ class YamahaDevice(MediaPlayerDevice): self._volume = 0 self._pwstate = STATE_OFF self._current_source = None + self._sound_mode = None + self._sound_mode_list = None self._source_list = None self._source_ignore = source_ignore or [] self._source_names = source_names or {} @@ -181,6 +184,8 @@ class YamahaDevice(MediaPlayerDevice): self._playback_support = self.receiver.get_playback_support() self._is_playback_supported = self.receiver.is_playback_supported( self._current_source) + self._sound_mode = self.receiver.surround_program + self._sound_mode_list = self.receiver.surround_programs() def build_source_list(self): """Build the source list.""" @@ -222,6 +227,16 @@ class YamahaDevice(MediaPlayerDevice): """Return the current input source.""" return self._current_source + @property + def sound_mode(self): + """Return the current sound mode.""" + return self._sound_mode + + @property + def sound_mode_list(self): + """Return the current sound mode.""" + return self._sound_mode_list + @property def source_list(self): """List of available input sources.""" @@ -330,6 +345,10 @@ class YamahaDevice(MediaPlayerDevice): """Enable or disable an output port..""" self.receiver.enable_output(port, enabled) + def select_sound_mode(self, sound_mode): + """Set Sound Mode for Receiver..""" + self.receiver.surround_program = sound_mode + @property def media_artist(self): """Artist of current playing media.""" From e75a1690d14ac23ae40db11a4cfb6afe085f4ab2 Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sat, 1 Sep 2018 23:37:03 +0200 Subject: [PATCH 012/357] Add unique_id to MQTT Light (#16303) * Add unique_id * Delete whitespaces --- homeassistant/components/light/mqtt.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/light/mqtt.py b/homeassistant/components/light/mqtt.py index 225f0f510ad..64331411f7f 100644 --- a/homeassistant/components/light/mqtt.py +++ b/homeassistant/components/light/mqtt.py @@ -54,6 +54,7 @@ CONF_WHITE_VALUE_SCALE = 'white_value_scale' CONF_WHITE_VALUE_STATE_TOPIC = 'white_value_state_topic' CONF_WHITE_VALUE_TEMPLATE = 'white_value_template' CONF_ON_COMMAND_TYPE = 'on_command_type' +CONF_UNIQUE_ID = 'unique_id' DEFAULT_BRIGHTNESS_SCALE = 255 DEFAULT_NAME = 'MQTT Light' @@ -79,6 +80,7 @@ PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({ vol.Optional(CONF_EFFECT_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_EFFECT_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_UNIQUE_ID): cv.string, vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean, vol.Optional(CONF_PAYLOAD_OFF, default=DEFAULT_PAYLOAD_OFF): cv.string, vol.Optional(CONF_PAYLOAD_ON, default=DEFAULT_PAYLOAD_ON): cv.string, @@ -111,6 +113,7 @@ async def async_setup_platform(hass, config, async_add_entities, async_add_entities([MqttLight( config.get(CONF_NAME), + config.get(CONF_UNIQUE_ID), config.get(CONF_EFFECT_LIST), { key: config.get(key) for key in ( @@ -159,14 +162,15 @@ async def async_setup_platform(hass, config, async_add_entities, class MqttLight(MqttAvailability, Light): """Representation of a MQTT light.""" - def __init__(self, name, effect_list, topic, templates, qos, - retain, payload, optimistic, brightness_scale, + def __init__(self, name, unique_id, effect_list, topic, templates, + qos, retain, payload, optimistic, brightness_scale, white_value_scale, on_command_type, availability_topic, payload_available, payload_not_available): """Initialize MQTT light.""" super().__init__(availability_topic, qos, payload_available, payload_not_available) self._name = name + self._unique_id = unique_id self._effect_list = effect_list self._topic = topic self._qos = qos @@ -392,6 +396,11 @@ class MqttLight(MqttAvailability, Light): """Return the name of the device if any.""" return self._name + @property + def unique_id(self): + """Return a unique ID.""" + return self._unique_id + @property def is_on(self): """Return true if device is on.""" From 2b0b431a2a5a6e1c0aa7862ee6e84c684b4db18b Mon Sep 17 00:00:00 2001 From: Jesse Rizzo <32472573+jesserizzo@users.noreply.github.com> Date: Sat, 1 Sep 2018 16:45:47 -0500 Subject: [PATCH 013/357] Update to EnvoyReader 0.2, support for more hardware (#16212) * Add support for older Envoy models * Stop requiring envoy model name in config * Update to envoy_reader0.2 * Minor formatting fixes * run script/gen_requirements_all.py * Minor formatting fixes * Change some strings to constants, use getattr to call function --- .../components/sensor/enphase_envoy.py | 35 +++++-------------- requirements_all.txt | 2 +- 2 files changed, 9 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/sensor/enphase_envoy.py b/homeassistant/components/sensor/enphase_envoy.py index 6afe887537c..7f8cff0f885 100644 --- a/homeassistant/components/sensor/enphase_envoy.py +++ b/homeassistant/components/sensor/enphase_envoy.py @@ -14,25 +14,27 @@ import homeassistant.helpers.config_validation as cv from homeassistant.const import (CONF_IP_ADDRESS, CONF_MONITORED_CONDITIONS) -REQUIREMENTS = ['envoy_reader==0.1'] +REQUIREMENTS = ['envoy_reader==0.2'] _LOGGER = logging.getLogger(__name__) SENSORS = { "production": ("Envoy Current Energy Production", 'W'), "daily_production": ("Envoy Today's Energy Production", "Wh"), - "7_days_production": ("Envoy Last Seven Days Energy Production", "Wh"), + "seven_days_production": ("Envoy Last Seven Days Energy Production", "Wh"), "lifetime_production": ("Envoy Lifetime Energy Production", "Wh"), "consumption": ("Envoy Current Energy Consumption", "W"), "daily_consumption": ("Envoy Today's Energy Consumption", "Wh"), - "7_days_consumption": ("Envoy Last Seven Days Energy Consumption", "Wh"), + "seven_days_consumption": ("Envoy Last Seven Days Energy Consumption", + "Wh"), "lifetime_consumption": ("Envoy Lifetime Energy Consumption", "Wh") } ICON = 'mdi:flash' +CONST_DEFAULT_HOST = "envoy" PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_IP_ADDRESS): cv.string, + vol.Optional(CONF_IP_ADDRESS, default=CONST_DEFAULT_HOST): cv.string, vol.Optional(CONF_MONITORED_CONDITIONS, default=list(SENSORS)): vol.All(cv.ensure_list, [vol.In(list(SENSORS))])}) @@ -81,27 +83,6 @@ class Envoy(Entity): def update(self): """Get the energy production data from the Enphase Envoy.""" - import envoy_reader + from envoy_reader import EnvoyReader - if self._type == "production": - self._state = int(envoy_reader.production(self._ip_address)) - elif self._type == "daily_production": - self._state = int(envoy_reader.daily_production(self._ip_address)) - elif self._type == "7_days_production": - self._state = int(envoy_reader.seven_days_production( - self._ip_address)) - elif self._type == "lifetime_production": - self._state = int(envoy_reader.lifetime_production( - self._ip_address)) - - elif self._type == "consumption": - self._state = int(envoy_reader.consumption(self._ip_address)) - elif self._type == "daily_consumption": - self._state = int(envoy_reader.daily_consumption( - self._ip_address)) - elif self._type == "7_days_consumption": - self._state = int(envoy_reader.seven_days_consumption( - self._ip_address)) - elif self._type == "lifetime_consumption": - self._state = int(envoy_reader.lifetime_consumption( - self._ip_address)) + self._state = getattr(EnvoyReader(self._ip_address), self._type)() diff --git a/requirements_all.txt b/requirements_all.txt index ff95a8fc2ad..8c2080d12fa 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -321,7 +321,7 @@ enocean==0.40 # envirophat==0.0.6 # homeassistant.components.sensor.enphase_envoy -envoy_reader==0.1 +envoy_reader==0.2 # homeassistant.components.sensor.season ephem==3.7.6.0 From 3797b6b012b79e384e20fecd9786a539b6941b22 Mon Sep 17 00:00:00 2001 From: Tod Schmidt Date: Sat, 1 Sep 2018 18:01:11 -0400 Subject: [PATCH 014/357] Snips: Added special slot values, session_id and slotname_raw (#16185) * Added special slot values, site_id, session_id, and slotname_raw * Update snips.py --- homeassistant/components/snips.py | 3 +++ tests/components/test_snips.py | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/snips.py b/homeassistant/components/snips.py index 34290819106..88a93408056 100644 --- a/homeassistant/components/snips.py +++ b/homeassistant/components/snips.py @@ -131,7 +131,10 @@ async def async_setup(hass, config): slots = {} for slot in request.get('slots', []): slots[slot['slotName']] = {'value': resolve_slot_values(slot)} + slots['{}_raw'.format(slot['slotName'])] = { + 'value': slot['rawValue']} slots['site_id'] = {'value': request.get('siteId')} + slots['session_id'] = {'value': request.get('sessionId')} slots['probability'] = {'value': request['intent']['probability']} try: diff --git a/tests/components/test_snips.py b/tests/components/test_snips.py index baeda2c49a8..bc044999bdd 100644 --- a/tests/components/test_snips.py +++ b/tests/components/test_snips.py @@ -93,6 +93,8 @@ async def test_snips_intent(hass, mqtt_mock): assert result payload = """ { + "siteId": "default", + "sessionId": "1234567890ABCDEF", "input": "turn the lights green", "intent": { "intentName": "Lights", @@ -104,7 +106,8 @@ async def test_snips_intent(hass, mqtt_mock): "value": { "kind": "Custom", "value": "green" - } + }, + "rawValue": "green" } ] } @@ -119,9 +122,12 @@ async def test_snips_intent(hass, mqtt_mock): intent = intents[0] assert intent.platform == 'snips' assert intent.intent_type == 'Lights' + assert intent assert intent.slots == {'light_color': {'value': 'green'}, + 'light_color_raw': {'value': 'green'}, 'probability': {'value': 1}, - 'site_id': {'value': None}} + 'site_id': {'value': 'default'}, + 'session_id': {'value': '1234567890ABCDEF'}} assert intent.text_input == 'turn the lights green' @@ -147,7 +153,8 @@ async def test_snips_service_intent(hass, mqtt_mock): "value": { "kind": "Custom", "value": "kitchen" - } + }, + "rawValue": "green" } ] } @@ -217,7 +224,9 @@ async def test_snips_intent_with_duration(hass, mqtt_mock): assert intent.intent_type == 'SetTimer' assert intent.slots == {'probability': {'value': 1}, 'site_id': {'value': None}, - 'timer_duration': {'value': 300}} + 'session_id': {'value': None}, + 'timer_duration': {'value': 300}, + 'timer_duration_raw': {'value': 'five minutes'}} async def test_intent_speech_response(hass, mqtt_mock): From 87eb6cd25a9bd100f707b9e2970cdd325cbef68d Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Sun, 2 Sep 2018 03:50:30 -0700 Subject: [PATCH 015/357] Upgrade hbmqtt to 0.9.4 (#16356) * Upgrade to hbmqtt 0.9.4 * Lint * Typo --- homeassistant/components/mqtt/server.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/mqtt/test_server.py | 6 ------ 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/mqtt/server.py b/homeassistant/components/mqtt/server.py index 5fc365342ae..57da85fe5f6 100644 --- a/homeassistant/components/mqtt/server.py +++ b/homeassistant/components/mqtt/server.py @@ -13,7 +13,7 @@ import voluptuous as vol from homeassistant.const import EVENT_HOMEASSISTANT_STOP import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['hbmqtt==0.9.2'] +REQUIREMENTS = ['hbmqtt==0.9.4'] DEPENDENCIES = ['http'] # None allows custom config to be created through generate_config diff --git a/requirements_all.txt b/requirements_all.txt index 8c2080d12fa..db244cc8cfb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -430,7 +430,7 @@ hangups==0.4.5 haversine==0.4.5 # homeassistant.components.mqtt.server -hbmqtt==0.9.2 +hbmqtt==0.9.4 # homeassistant.components.climate.heatmiser heatmiserV3==0.9.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bd921085461..446e3d056a3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -78,7 +78,7 @@ hangups==0.4.5 haversine==0.4.5 # homeassistant.components.mqtt.server -hbmqtt==0.9.2 +hbmqtt==0.9.4 # homeassistant.components.binary_sensor.workday holidays==0.9.6 diff --git a/tests/components/mqtt/test_server.py b/tests/components/mqtt/test_server.py index c761c47542f..976fdd3d15c 100644 --- a/tests/components/mqtt/test_server.py +++ b/tests/components/mqtt/test_server.py @@ -1,8 +1,5 @@ """The tests for the MQTT component embedded server.""" from unittest.mock import Mock, MagicMock, patch -import sys - -import pytest from homeassistant.const import CONF_PASSWORD from homeassistant.setup import setup_component @@ -11,9 +8,6 @@ import homeassistant.components.mqtt as mqtt from tests.common import get_test_home_assistant, mock_coro -# Until https://github.com/beerfactory/hbmqtt/pull/139 is released -@pytest.mark.skipif(sys.version_info[:2] >= (3, 7), - reason='Package incompatible with Python 3.7') class TestMQTT: """Test the MQTT component.""" From 03fb2b32a6a8ef6becbab41237bf6720ae94838d Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 2 Sep 2018 12:51:07 +0200 Subject: [PATCH 016/357] Upgrade Sphinx to 1.7.7 (#16359) --- requirements_docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_docs.txt b/requirements_docs.txt index a7436cad2fc..e26c97887b7 100644 --- a/requirements_docs.txt +++ b/requirements_docs.txt @@ -1,3 +1,3 @@ -Sphinx==1.7.6 +Sphinx==1.7.7 sphinx-autodoc-typehints==1.3.0 sphinx-autodoc-annotation==1.0.post1 From 15ad82b9bdc384beb3fa8b31c9c39ad9372ec865 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 2 Sep 2018 12:51:25 +0200 Subject: [PATCH 017/357] Upgrade qnapstats to 0.2.7 (#16360) --- homeassistant/components/sensor/qnap.py | 4 ++-- requirements_all.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensor/qnap.py b/homeassistant/components/sensor/qnap.py index 22f8d4c37c0..29eb8cd6749 100644 --- a/homeassistant/components/sensor/qnap.py +++ b/homeassistant/components/sensor/qnap.py @@ -17,7 +17,7 @@ from homeassistant.const import ( from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['qnapstats==0.2.6'] +REQUIREMENTS = ['qnapstats==0.2.7'] _LOGGER = logging.getLogger(__name__) @@ -173,7 +173,7 @@ class QNAPStatsAPI: protocol = "https" if config.get(CONF_SSL) else "http" self._api = QNAPStats( - protocol + "://" + config.get(CONF_HOST), + '{}://{}'.format(protocol, config.get(CONF_HOST)), config.get(CONF_PORT), config.get(CONF_USERNAME), config.get(CONF_PASSWORD), diff --git a/requirements_all.txt b/requirements_all.txt index db244cc8cfb..49fc3b42466 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1217,7 +1217,7 @@ pyxeoma==1.4.0 pyzabbix==0.7.4 # homeassistant.components.sensor.qnap -qnapstats==0.2.6 +qnapstats==0.2.7 # homeassistant.components.rachio rachiopy==0.1.3 From 1d12c7b0e7d30348e9f5f5cf7e52eecffba91a6d Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 2 Sep 2018 12:51:36 +0200 Subject: [PATCH 018/357] Upgrade mutagen to 1.41.1 (#16361) --- homeassistant/components/tts/__init__.py | 12 ++++++------ requirements_all.txt | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/tts/__init__.py b/homeassistant/components/tts/__init__.py index f060c9f353a..2ec9a2ab801 100644 --- a/homeassistant/components/tts/__init__.py +++ b/homeassistant/components/tts/__init__.py @@ -29,7 +29,7 @@ from homeassistant.helpers import config_per_platform import homeassistant.helpers.config_validation as cv from homeassistant.setup import async_prepare_setup_platform -REQUIREMENTS = ['mutagen==1.41.0'] +REQUIREMENTS = ['mutagen==1.41.1'] _LOGGER = logging.getLogger(__name__) @@ -69,8 +69,8 @@ PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA.extend({ SCHEMA_SERVICE_SAY = vol.Schema({ vol.Required(ATTR_MESSAGE): cv.string, - vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, vol.Optional(ATTR_CACHE): cv.boolean, + vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, vol.Optional(ATTR_LANGUAGE): cv.string, vol.Optional(ATTR_OPTIONS): dict, }) @@ -117,7 +117,7 @@ async def async_setup(hass, config): tts.async_register_engine(p_type, provider, p_config) except Exception: # pylint: disable=broad-except - _LOGGER.exception("Error setting up platform %s", p_type) + _LOGGER.exception("Error setting up platform: %s", p_type) return async def async_say_handle(service): @@ -134,7 +134,7 @@ async def async_setup(hass, config): options=options ) except HomeAssistantError as err: - _LOGGER.error("Error on init tts: %s", err) + _LOGGER.error("Error on init TTS: %s", err) return data = { @@ -302,8 +302,8 @@ class SpeechManager: return "{}/api/tts_proxy/{}".format( self.hass.config.api.base_url, filename) - async def async_get_tts_audio(self, engine, key, message, cache, language, - options): + async def async_get_tts_audio( + self, engine, key, message, cache, language, options): """Receive TTS and store for view in cache. This method is a coroutine. diff --git a/requirements_all.txt b/requirements_all.txt index 49fc3b42466..64a9d6bc133 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -585,7 +585,7 @@ mitemp_bt==0.0.1 motorparts==1.0.2 # homeassistant.components.tts -mutagen==1.41.0 +mutagen==1.41.1 # homeassistant.components.mychevy mychevy==0.4.0 From 97695a30f5f9af6ffd4a399d404290a97fb428ae Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 2 Sep 2018 12:51:56 +0200 Subject: [PATCH 019/357] Upgrade shodan to 1.10.0 (#16363) --- homeassistant/components/sensor/shodan.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/shodan.py b/homeassistant/components/sensor/shodan.py index fd462d6811c..cfafcb67608 100644 --- a/homeassistant/components/sensor/shodan.py +++ b/homeassistant/components/sensor/shodan.py @@ -14,7 +14,7 @@ from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ATTR_ATTRIBUTION, CONF_API_KEY, CONF_NAME from homeassistant.helpers.entity import Entity -REQUIREMENTS = ['shodan==1.9.1'] +REQUIREMENTS = ['shodan==1.10.0'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 64a9d6bc133..1781a70c6ca 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1290,7 +1290,7 @@ sense_energy==0.4.1 sharp_aquos_rc==0.3.2 # homeassistant.components.sensor.shodan -shodan==1.9.1 +shodan==1.10.0 # homeassistant.components.notify.simplepush simplepush==1.1.4 From 52e922171daa5ea9ff7f33651d6c1b650c918b61 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 2 Sep 2018 14:33:20 +0200 Subject: [PATCH 020/357] Upgrade to youtube_dl to 2018.09.01 (#16365) --- homeassistant/components/media_extractor.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_extractor.py b/homeassistant/components/media_extractor.py index 8f2abb9be19..f6e982a77ef 100644 --- a/homeassistant/components/media_extractor.py +++ b/homeassistant/components/media_extractor.py @@ -14,7 +14,7 @@ from homeassistant.components.media_player import ( SERVICE_PLAY_MEDIA) from homeassistant.helpers import config_validation as cv -REQUIREMENTS = ['youtube_dl==2018.08.22'] +REQUIREMENTS = ['youtube_dl==2018.09.01'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index 1781a70c6ca..b2ac021d61e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1512,7 +1512,7 @@ yeelight==0.4.0 yeelightsunflower==0.0.10 # homeassistant.components.media_extractor -youtube_dl==2018.08.22 +youtube_dl==2018.09.01 # homeassistant.components.light.zengge zengge==0.2 From 357e5eadb87a5fd8159c98ea17f1335ed5cf374a Mon Sep 17 00:00:00 2001 From: MarcSN311 Date: Sun, 2 Sep 2018 15:51:15 +0200 Subject: [PATCH 021/357] Added 'nomapnt', 'outcurnt', 'loadapnt' fields (#16176) * Added 'nomapnt', 'outcurnt', 'loadapnt' fields Also added Ampere and Volt-Ampere to INFERRED_UNITS * Fix lint issue --- homeassistant/components/sensor/apcupsd.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/homeassistant/components/sensor/apcupsd.py b/homeassistant/components/sensor/apcupsd.py index 897f0626b7c..90c1f2e6795 100644 --- a/homeassistant/components/sensor/apcupsd.py +++ b/homeassistant/components/sensor/apcupsd.py @@ -49,6 +49,7 @@ SENSOR_TYPES = { 'linefreq': ['Line Frequency', 'Hz', 'mdi:information-outline'], 'linev': ['Input Voltage', 'V', 'mdi:flash'], 'loadpct': ['Load', '%', 'mdi:gauge'], + 'loadapnt': ['Load Apparent Power', '%', 'mdi:gauge'], 'lotrans': ['Transfer Low', 'V', 'mdi:flash'], 'mandate': ['Manufacture Date', '', 'mdi:calendar'], 'masterupd': ['Master Update', '', 'mdi:information-outline'], @@ -62,7 +63,9 @@ SENSOR_TYPES = { 'nominv': ['Nominal Input Voltage', 'V', 'mdi:flash'], 'nomoutv': ['Nominal Output Voltage', 'V', 'mdi:flash'], 'nompower': ['Nominal Output Power', 'W', 'mdi:flash'], + 'nomapnt': ['Nominal Apparent Power', 'VA', 'mdi:flash'], 'numxfers': ['Transfer Count', '', 'mdi:counter'], + 'outcurnt': ['Output Current', 'A', 'mdi:flash'], 'outputv': ['Output Voltage', 'V', 'mdi:flash'], 'reg1': ['Register 1 Fault', '', 'mdi:information-outline'], 'reg2': ['Register 2 Fault', '', 'mdi:information-outline'], @@ -93,6 +96,8 @@ INFERRED_UNITS = { ' Seconds': 'sec', ' Percent': '%', ' Volts': 'V', + ' Ampere': 'A', + ' Volt-Ampere': 'VA', ' Watts': 'W', ' Hz': 'Hz', ' C': TEMP_CELSIUS, From b29c296ced611231ec8000d2cbe8425b4c130eae Mon Sep 17 00:00:00 2001 From: Lev Aronsky Date: Sun, 2 Sep 2018 17:42:08 +0300 Subject: [PATCH 022/357] Generic Thermostat: add support for climate.turn_on/climate.turn_off (#16080) * Added async_turn_on and async_turn_off implementations. * Added turning on/off tests to generic thermostat * style * style * style --- .../components/climate/generic_thermostat.py | 8 ++ .../climate/test_generic_thermostat.py | 85 +++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/homeassistant/components/climate/generic_thermostat.py b/homeassistant/components/climate/generic_thermostat.py index fec18329878..85879b8122a 100644 --- a/homeassistant/components/climate/generic_thermostat.py +++ b/homeassistant/components/climate/generic_thermostat.py @@ -251,6 +251,14 @@ class GenericThermostat(ClimateDevice): # Ensure we update the current operation after changing the mode self.schedule_update_ha_state() + async def async_turn_on(self): + """Turn thermostat on.""" + await self.async_set_operation_mode(self.operation_list[0]) + + async def async_turn_off(self): + """Turn thermostat off.""" + await self.async_set_operation_mode(STATE_OFF) + async def async_set_temperature(self, **kwargs): """Set new target temperature.""" temperature = kwargs.get(ATTR_TEMPERATURE) diff --git a/tests/components/climate/test_generic_thermostat.py b/tests/components/climate/test_generic_thermostat.py index ac587db13fa..c4e07705230 100644 --- a/tests/components/climate/test_generic_thermostat.py +++ b/tests/components/climate/test_generic_thermostat.py @@ -21,6 +21,7 @@ from homeassistant import loader from homeassistant.util.unit_system import METRIC_SYSTEM from homeassistant.util.async_ import run_coroutine_threadsafe from homeassistant.components import climate, input_boolean, switch +from homeassistant.components.climate import STATE_HEAT, STATE_COOL import homeassistant.components as comps from tests.common import (assert_setup_component, get_test_home_assistant, mock_restore_cache) @@ -894,6 +895,90 @@ class TestClimateGenericThermostatKeepAlive(unittest.TestCase): self.hass.services.register(ha.DOMAIN, SERVICE_TURN_OFF, log_call) +class TestClimateGenericThermostatTurnOnOff(unittest.TestCase): + """Test the Generic Thermostat.""" + + HEAT_ENTITY = 'climate.test_heat' + COOL_ENTITY = 'climate.test_cool' + + def setUp(self): # pylint: disable=invalid-name + """Set up things to be run when tests are started.""" + self.hass = get_test_home_assistant() + assert setup_component(self.hass, climate.DOMAIN, {'climate': [ + { + 'platform': 'generic_thermostat', + 'name': 'test_heat', + 'heater': ENT_SWITCH, + 'target_sensor': ENT_SENSOR + }, + { + 'platform': 'generic_thermostat', + 'name': 'test_cool', + 'heater': ENT_SWITCH, + 'ac_mode': True, + 'target_sensor': ENT_SENSOR + } + ]}) + + def tearDown(self): # pylint: disable=invalid-name + """Stop down everything that was started.""" + self.hass.stop() + + def test_turn_on_when_off(self): + """Test if climate.turn_on turns on a turned off device.""" + climate.set_operation_mode(self.hass, STATE_OFF) + self.hass.block_till_done() + self.hass.services.call('climate', SERVICE_TURN_ON) + self.hass.block_till_done() + state_heat = self.hass.states.get(self.HEAT_ENTITY) + state_cool = self.hass.states.get(self.COOL_ENTITY) + self.assertEqual(STATE_HEAT, + state_heat.attributes.get('operation_mode')) + self.assertEqual(STATE_COOL, + state_cool.attributes.get('operation_mode')) + + def test_turn_on_when_on(self): + """Test if climate.turn_on does nothing to a turned on device.""" + climate.set_operation_mode(self.hass, STATE_HEAT, self.HEAT_ENTITY) + climate.set_operation_mode(self.hass, STATE_COOL, self.COOL_ENTITY) + self.hass.block_till_done() + self.hass.services.call('climate', SERVICE_TURN_ON) + self.hass.block_till_done() + state_heat = self.hass.states.get(self.HEAT_ENTITY) + state_cool = self.hass.states.get(self.COOL_ENTITY) + self.assertEqual(STATE_HEAT, + state_heat.attributes.get('operation_mode')) + self.assertEqual(STATE_COOL, + state_cool.attributes.get('operation_mode')) + + def test_turn_off_when_on(self): + """Test if climate.turn_off turns off a turned on device.""" + climate.set_operation_mode(self.hass, STATE_HEAT, self.HEAT_ENTITY) + climate.set_operation_mode(self.hass, STATE_COOL, self.COOL_ENTITY) + self.hass.block_till_done() + self.hass.services.call('climate', SERVICE_TURN_OFF) + self.hass.block_till_done() + state_heat = self.hass.states.get(self.HEAT_ENTITY) + state_cool = self.hass.states.get(self.COOL_ENTITY) + self.assertEqual(STATE_OFF, + state_heat.attributes.get('operation_mode')) + self.assertEqual(STATE_OFF, + state_cool.attributes.get('operation_mode')) + + def test_turn_off_when_off(self): + """Test if climate.turn_off does nothing to a turned off device.""" + climate.set_operation_mode(self.hass, STATE_OFF) + self.hass.block_till_done() + self.hass.services.call('climate', SERVICE_TURN_OFF) + self.hass.block_till_done() + state_heat = self.hass.states.get(self.HEAT_ENTITY) + state_cool = self.hass.states.get(self.COOL_ENTITY) + self.assertEqual(STATE_OFF, + state_heat.attributes.get('operation_mode')) + self.assertEqual(STATE_OFF, + state_cool.attributes.get('operation_mode')) + + @asyncio.coroutine def test_custom_setup_params(hass): """Test the setup with custom parameters.""" From a5cff9877e343cdd5754f4e4d6fac77445ed1f68 Mon Sep 17 00:00:00 2001 From: Martin Fuchs <39280548+fucm@users.noreply.github.com> Date: Sun, 2 Sep 2018 17:02:51 +0200 Subject: [PATCH 023/357] Add support for Tahoma Lighting Receiver on/off io (#15925) * Add support for Tahoma light switch * Clean up attributes and add available method * Remove else statement --- homeassistant/components/switch/tahoma.py | 70 ++++++++++++++++++++++- homeassistant/components/tahoma.py | 1 + 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switch/tahoma.py b/homeassistant/components/switch/tahoma.py index 2904f06b432..bcac038d43b 100644 --- a/homeassistant/components/switch/tahoma.py +++ b/homeassistant/components/switch/tahoma.py @@ -12,11 +12,14 @@ import logging from homeassistant.components.switch import SwitchDevice from homeassistant.components.tahoma import ( DOMAIN as TAHOMA_DOMAIN, TahomaDevice) +from homeassistant.const import (STATE_OFF, STATE_ON) DEPENDENCIES = ['tahoma'] _LOGGER = logging.getLogger(__name__) +ATTR_RSSI_LEVEL = 'rssi_level' + def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tahoma switches.""" @@ -30,6 +33,33 @@ def setup_platform(hass, config, add_entities, discovery_info=None): class TahomaSwitch(TahomaDevice, SwitchDevice): """Representation a Tahoma Switch.""" + def __init__(self, tahoma_device, controller): + """Initialize the switch.""" + super().__init__(tahoma_device, controller) + self._state = STATE_OFF + self._skip_update = False + self._available = False + + def update(self): + """Update method.""" + # Postpone the immediate state check for changes that take time. + if self._skip_update: + self._skip_update = False + return + + self.controller.get_states([self.tahoma_device]) + + if self.tahoma_device.type == 'io:OnOffLightIOComponent': + if self.tahoma_device.active_states.get('core:OnOffState') == 'on': + self._state = STATE_ON + else: + self._state = STATE_OFF + + self._available = bool(self.tahoma_device.active_states.get( + 'core:StatusState') == 'available') + + _LOGGER.debug("Update %s, state: %s", self._name, self._state) + @property def device_class(self): """Return the class of the device.""" @@ -39,7 +69,23 @@ class TahomaSwitch(TahomaDevice, SwitchDevice): def turn_on(self, **kwargs): """Send the on command.""" - self.toggle() + _LOGGER.debug("Turn on: %s", self._name) + if self.tahoma_device.type == 'rts:GarageDoor4TRTSComponent': + self.toggle() + else: + self.apply_action('on') + self._skip_update = True + self._state = STATE_ON + + def turn_off(self, **kwargs): + """Send the off command.""" + _LOGGER.debug("Turn off: %s", self._name) + if self.tahoma_device.type == 'rts:GarageDoor4TRTSComponent': + return + + self.apply_action('off') + self._skip_update = True + self._state = STATE_OFF def toggle(self, **kwargs): """Click the switch.""" @@ -48,4 +94,24 @@ class TahomaSwitch(TahomaDevice, SwitchDevice): @property def is_on(self): """Get whether the switch is in on state.""" - return False + if self.tahoma_device.type == 'rts:GarageDoor4TRTSComponent': + return False + return bool(self._state == STATE_ON) + + @property + def device_state_attributes(self): + """Return the device state attributes.""" + attr = {} + super_attr = super().device_state_attributes + if super_attr is not None: + attr.update(super_attr) + + if 'core:RSSILevelState' in self.tahoma_device.active_states: + attr[ATTR_RSSI_LEVEL] = \ + self.tahoma_device.active_states['core:RSSILevelState'] + return attr + + @property + def available(self): + """Return True if entity is available.""" + return self._available diff --git a/homeassistant/components/tahoma.py b/homeassistant/components/tahoma.py index aaa64489168..64071ddb037 100644 --- a/homeassistant/components/tahoma.py +++ b/homeassistant/components/tahoma.py @@ -52,6 +52,7 @@ TAHOMA_TYPES = { 'rts:GarageDoor4TRTSComponent': 'switch', 'io:VerticalExteriorAwningIOComponent': 'cover', 'io:HorizontalAwningIOComponent': 'cover', + 'io:OnOffLightIOComponent': 'switch', 'rtds:RTDSSmokeSensor': 'smoke', } From 03480dc779e2837cec0f4db16b8270a31257d86d Mon Sep 17 00:00:00 2001 From: Totoo Date: Sun, 2 Sep 2018 18:58:31 +0200 Subject: [PATCH 024/357] Update discord.py (#16248) * Update discord.py * Update discord.py Fixed ATTR_IMAGES checking, line length, and ATTR_DATA imported. Also fixed missing spaces. * Update discord.py Fix E302... --- homeassistant/components/notify/discord.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/notify/discord.py b/homeassistant/components/notify/discord.py index dca47a46dbf..0cf4bced360 100644 --- a/homeassistant/components/notify/discord.py +++ b/homeassistant/components/notify/discord.py @@ -11,7 +11,7 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.components.notify import ( - PLATFORM_SCHEMA, BaseNotificationService, ATTR_TARGET) + PLATFORM_SCHEMA, BaseNotificationService, ATTR_TARGET, ATTR_DATA) from homeassistant.const import CONF_TOKEN _LOGGER = logging.getLogger(__name__) @@ -22,6 +22,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_TOKEN): cv.string }) +ATTR_IMAGES = 'images' + def get_service(hass, config, discovery_info=None): """Get the Discord notification service.""" @@ -53,9 +55,15 @@ class DiscordNotificationService(BaseNotificationService): def on_ready(): """Send the messages when the bot is ready.""" try: + data = kwargs.get(ATTR_DATA) + if data: + images = data.get(ATTR_IMAGES) for channelid in kwargs[ATTR_TARGET]: channel = discord.Object(id=channelid) yield from discord_bot.send_message(channel, message) + if images: + for anum, f_name in enumerate(images): + yield from discord_bot.send_file(channel, f_name) except (discord.errors.HTTPException, discord.errors.NotFound) as error: _LOGGER.warning("Communication error: %s", error) From ac3700d1c46959bb572b5d0b73936fa0491806c7 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 2 Sep 2018 19:01:25 +0200 Subject: [PATCH 025/357] Upgrade python-telegram-bot to 11.0.0 (#16373) --- homeassistant/components/telegram_bot/__init__.py | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/telegram_bot/__init__.py b/homeassistant/components/telegram_bot/__init__.py index 53695102601..8e24716ab57 100644 --- a/homeassistant/components/telegram_bot/__init__.py +++ b/homeassistant/components/telegram_bot/__init__.py @@ -22,7 +22,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.exceptions import TemplateError from homeassistant.setup import async_prepare_setup_platform -REQUIREMENTS = ['python-telegram-bot==10.1.0'] +REQUIREMENTS = ['python-telegram-bot==11.0.0'] _LOGGER = logging.getLogger(__name__) diff --git a/requirements_all.txt b/requirements_all.txt index b2ac021d61e..cf761ef4aa6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1139,7 +1139,7 @@ python-synology==0.2.0 python-tado==0.2.3 # homeassistant.components.telegram_bot -python-telegram-bot==10.1.0 +python-telegram-bot==11.0.0 # homeassistant.components.sensor.twitch python-twitch==1.3.0 From 78fcea25bbfcf36e8b646941896f9409169ac5b1 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 2 Sep 2018 19:01:43 +0200 Subject: [PATCH 026/357] Upgrade attrs to 18.2.0 (#16372) --- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 3e9a763181a..8d49d5d07c6 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -1,7 +1,7 @@ aiohttp==3.4.0 astral==1.6.1 async_timeout==3.0.0 -attrs==18.1.0 +attrs==18.2.0 bcrypt==3.1.4 certifi>=2018.04.16 jinja2>=2.10 diff --git a/requirements_all.txt b/requirements_all.txt index cf761ef4aa6..41814e84b64 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2,7 +2,7 @@ aiohttp==3.4.0 astral==1.6.1 async_timeout==3.0.0 -attrs==18.1.0 +attrs==18.2.0 bcrypt==3.1.4 certifi>=2018.04.16 jinja2>=2.10 diff --git a/setup.py b/setup.py index b1b0af70319..71f63adfb64 100755 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ REQUIRES = [ 'aiohttp==3.4.0', 'astral==1.6.1', 'async_timeout==3.0.0', - 'attrs==18.1.0', + 'attrs==18.2.0', 'bcrypt==3.1.4', 'certifi>=2018.04.16', 'jinja2>=2.10', From 4685a2cd97452ce5ecd679af1a3bba3602be20e9 Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Sun, 2 Sep 2018 13:17:29 -0700 Subject: [PATCH 027/357] Update server.py (#16375) --- homeassistant/components/mqtt/server.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homeassistant/components/mqtt/server.py b/homeassistant/components/mqtt/server.py index 57da85fe5f6..45529411ed5 100644 --- a/homeassistant/components/mqtt/server.py +++ b/homeassistant/components/mqtt/server.py @@ -85,6 +85,10 @@ def generate_config(hass, passwd, password): 'allow-anonymous': password is None }, 'plugins': ['auth_anonymous'], + 'topic-check': { + 'enabled': True, + 'plugins': ['topic_taboo'], + }, } if password: From 1966597d5ef4fd8536401d3531ebd219aa9f4de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Sun, 2 Sep 2018 23:05:48 +0200 Subject: [PATCH 028/357] add_entities for switchmate (#16368) --- homeassistant/components/switch/switchmate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switch/switchmate.py b/homeassistant/components/switch/switchmate.py index 6ce4421ebc8..7ccd3bee4b6 100644 --- a/homeassistant/components/switch/switchmate.py +++ b/homeassistant/components/switch/switchmate.py @@ -31,11 +31,11 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ }) -def setup_platform(hass, config, add_devices, discovery_info=None) -> None: +def setup_platform(hass, config, add_entities, discovery_info=None) -> None: """Perform the setup for Switchmate devices.""" name = config.get(CONF_NAME) mac_addr = config.get(CONF_MAC) - add_devices([Switchmate(mac_addr, name)], True) + add_entities([Switchmate(mac_addr, name)], True) class Switchmate(SwitchDevice): From bf29cbd38184310960f26606d3d4ebcb69066178 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 3 Sep 2018 13:20:57 +0200 Subject: [PATCH 029/357] Update frontend to 20180903.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 5508aa76acf..72f61ddf1eb 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -26,7 +26,7 @@ from homeassistant.helpers.translation import async_get_translations from homeassistant.loader import bind_hass from homeassistant.util.yaml import load_yaml -REQUIREMENTS = ['home-assistant-frontend==20180831.0'] +REQUIREMENTS = ['home-assistant-frontend==20180903.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index 41814e84b64..6223f9cc51c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -448,7 +448,7 @@ hole==0.3.0 holidays==0.9.6 # homeassistant.components.frontend -home-assistant-frontend==20180831.0 +home-assistant-frontend==20180903.0 # homeassistant.components.homekit_controller # homekit==0.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 446e3d056a3..0ee02e0d109 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -84,7 +84,7 @@ hbmqtt==0.9.4 holidays==0.9.6 # homeassistant.components.frontend -home-assistant-frontend==20180831.0 +home-assistant-frontend==20180903.0 # homeassistant.components.homematicip_cloud homematicip==0.9.8 From 669b3458b9029d55ba79dbbfab79b803508467f6 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 3 Sep 2018 13:20:57 +0200 Subject: [PATCH 030/357] Update frontend to 20180903.0 --- homeassistant/components/frontend/__init__.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 5508aa76acf..72f61ddf1eb 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -26,7 +26,7 @@ from homeassistant.helpers.translation import async_get_translations from homeassistant.loader import bind_hass from homeassistant.util.yaml import load_yaml -REQUIREMENTS = ['home-assistant-frontend==20180831.0'] +REQUIREMENTS = ['home-assistant-frontend==20180903.0'] DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log', diff --git a/requirements_all.txt b/requirements_all.txt index 6e9cbeffc7d..7060e5dae30 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -442,7 +442,7 @@ hole==0.3.0 holidays==0.9.6 # homeassistant.components.frontend -home-assistant-frontend==20180831.0 +home-assistant-frontend==20180903.0 # homeassistant.components.homekit_controller # homekit==0.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 57d4dd9b996..106341aa5fb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -84,7 +84,7 @@ hbmqtt==0.9.2 holidays==0.9.6 # homeassistant.components.frontend -home-assistant-frontend==20180831.0 +home-assistant-frontend==20180903.0 # homeassistant.components.homematicip_cloud homematicip==0.9.8 From a2a447b466f658d47aea9719297466d02100c775 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 3 Sep 2018 13:21:37 +0200 Subject: [PATCH 031/357] Update translations --- .../components/auth/.translations/hu.json | 16 ++++++++++ .../components/auth/.translations/ko.json | 2 +- .../components/cast/.translations/fr.json | 6 ++-- .../components/deconz/.translations/no.json | 2 +- .../components/hangouts/.translations/fr.json | 7 +++-- .../components/hangouts/.translations/hu.json | 29 +++++++++++++++++++ .../hangouts/.translations/pt-BR.json | 6 +++- .../homematicip_cloud/.translations/no.json | 2 +- .../components/nest/.translations/fr.json | 18 +++++++++++- .../sensor/.translations/moon.hu.json | 12 ++++++++ .../sensor/.translations/moon.no.json | 4 +-- .../sensor/.translations/moon.pt-BR.json | 6 ++-- .../components/sonos/.translations/fr.json | 13 +++++++++ 13 files changed, 110 insertions(+), 13 deletions(-) create mode 100644 homeassistant/components/auth/.translations/hu.json create mode 100644 homeassistant/components/hangouts/.translations/hu.json create mode 100644 homeassistant/components/sensor/.translations/moon.hu.json create mode 100644 homeassistant/components/sonos/.translations/fr.json diff --git a/homeassistant/components/auth/.translations/hu.json b/homeassistant/components/auth/.translations/hu.json new file mode 100644 index 00000000000..4500098553e --- /dev/null +++ b/homeassistant/components/auth/.translations/hu.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "\u00c9rv\u00e9nytelen k\u00f3d, pr\u00f3b\u00e1ld \u00fajra. Ha ez a hiba folyamatosan el\u0151fordul, akkor gy\u0151z\u0151dj meg r\u00f3la, hogy a Home Assistant rendszered \u00f3r\u00e1ja pontosan j\u00e1r." + }, + "step": { + "init": { + "description": "Ahhoz, hogy haszn\u00e1lhasd a k\u00e9tfaktoros hiteles\u00edt\u00e9st id\u0151alap\u00fa egyszeri jelszavakkal, szkenneld be a QR k\u00f3dot a hiteles\u00edt\u00e9si applik\u00e1ci\u00f3ddal. Ha m\u00e9g nincsen, akkor a [Google Hiteles\u00edt\u0151](https://support.google.com/accounts/answer/1066447)t vagy az [Authy](https://authy.com/)-t aj\u00e1nljuk.\n\n{qr_code}\n\nA k\u00f3d beolvas\u00e1sa ut\u00e1n add meg a hat sz\u00e1mjegy\u0171 k\u00f3dot az applik\u00e1ci\u00f3b\u00f3l a telep\u00edt\u00e9s ellen\u0151rz\u00e9s\u00e9hez. Ha probl\u00e9m\u00e1ba \u00fctk\u00f6z\u00f6l a QR k\u00f3d beolvas\u00e1s\u00e1n\u00e1l, akkor ind\u00edts egy k\u00e9zi be\u00e1ll\u00edt\u00e1st a **`{code}`** k\u00f3ddal.", + "title": "K\u00e9tfaktoros hiteles\u00edt\u00e9s be\u00e1ll\u00edt\u00e1sa TOTP haszn\u00e1lat\u00e1val" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/ko.json b/homeassistant/components/auth/.translations/ko.json index 4eb4783edd9..17fb5c56f57 100644 --- a/homeassistant/components/auth/.translations/ko.json +++ b/homeassistant/components/auth/.translations/ko.json @@ -6,7 +6,7 @@ }, "step": { "init": { - "description": "\uc2dc\uac04 \uae30\ubc18\uc758 \uc77c\ud68c\uc6a9 \ube44\ubc00\ubc88\ud638\ub97c \uc0ac\uc6a9\ud558\ub294 2\ub2e8\uacc4 \uc778\uc99d\uc744 \ud558\ub824\uba74 \uc778\uc99d\uc6a9 \uc571\uc744 \uc774\uc6a9\ud574\uc11c QR \ucf54\ub4dc\ub97c \uc2a4\uce94\ud574 \uc8fc\uc138\uc694. \uc778\uc99d\uc6a9 \uc571\uc740 [Google Authenticator](https://support.google.com/accounts/answer/1066447) \ub610\ub294 [Authy](https://authy.com/) \ub97c \ucd94\ucc9c\ub4dc\ub9bd\ub2c8\ub2e4.\n\n{qr_code}\n\n\uc2a4\uce94 \ud6c4\uc5d0 \uc0dd\uc131\ub41c 6\uc790\ub9ac \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc11c \uc124\uc815\uc744 \ud655\uc778\ud558\uc138\uc694. QR \ucf54\ub4dc \uc2a4\uce94\uc5d0 \ubb38\uc81c\uac00 \uc788\ub2e4\uba74, **`{code}`** \ucf54\ub4dc\ub85c \uc9c1\uc811 \uc124\uc815\ud574\ubcf4\uc138\uc694.", + "description": "\uc2dc\uac04 \uae30\ubc18\uc758 \uc77c\ud68c\uc6a9 \ube44\ubc00\ubc88\ud638\ub97c \uc0ac\uc6a9\ud558\ub294 2\ub2e8\uacc4 \uc778\uc99d\uc744 \ud558\ub824\uba74 \uc778\uc99d\uc6a9 \uc571\uc744 \uc774\uc6a9\ud574\uc11c QR \ucf54\ub4dc\ub97c \uc2a4\uce94\ud574 \uc8fc\uc138\uc694. \uc778\uc99d\uc6a9 \uc571\uc740 [Google OTP](https://support.google.com/accounts/answer/1066447) \ub610\ub294 [Authy](https://authy.com/) \ub97c \ucd94\ucc9c\ub4dc\ub9bd\ub2c8\ub2e4.\n\n{qr_code}\n\n\uc2a4\uce94 \ud6c4\uc5d0 \uc0dd\uc131\ub41c 6\uc790\ub9ac \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc11c \uc124\uc815\uc744 \ud655\uc778\ud558\uc138\uc694. QR \ucf54\ub4dc \uc2a4\uce94\uc5d0 \ubb38\uc81c\uac00 \uc788\ub2e4\uba74, **`{code}`** \ucf54\ub4dc\ub85c \uc9c1\uc811 \uc124\uc815\ud574\ubcf4\uc138\uc694.", "title": "TOTP \ub97c \uc0ac\uc6a9\ud558\uc5ec 2 \ub2e8\uacc4 \uc778\uc99d \uad6c\uc131" } }, diff --git a/homeassistant/components/cast/.translations/fr.json b/homeassistant/components/cast/.translations/fr.json index acacddf2187..d3b95121de6 100644 --- a/homeassistant/components/cast/.translations/fr.json +++ b/homeassistant/components/cast/.translations/fr.json @@ -6,8 +6,10 @@ }, "step": { "confirm": { - "description": "Voulez-vous configurer Google Cast?" + "description": "Voulez-vous configurer Google Cast?", + "title": "Google Cast" } - } + }, + "title": "Google Cast" } } \ No newline at end of file diff --git a/homeassistant/components/deconz/.translations/no.json b/homeassistant/components/deconz/.translations/no.json index 55518b7da53..27868814eab 100644 --- a/homeassistant/components/deconz/.translations/no.json +++ b/homeassistant/components/deconz/.translations/no.json @@ -28,6 +28,6 @@ "title": "Ekstra konfigurasjonsalternativer for deCONZ" } }, - "title": "deCONZ" + "title": "deCONZ Zigbee gateway" } } \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/fr.json b/homeassistant/components/hangouts/.translations/fr.json index c92d478c454..53759f9b534 100644 --- a/homeassistant/components/hangouts/.translations/fr.json +++ b/homeassistant/components/hangouts/.translations/fr.json @@ -13,9 +13,12 @@ }, "user": { "data": { + "email": "Adresse e-mail", "password": "Mot de passe" - } + }, + "title": "Connexion \u00e0 Google Hangouts" } - } + }, + "title": "Google Hangouts" } } \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/hu.json b/homeassistant/components/hangouts/.translations/hu.json new file mode 100644 index 00000000000..2631843c784 --- /dev/null +++ b/homeassistant/components/hangouts/.translations/hu.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "A Google Hangouts m\u00e1r konfigur\u00e1lva van", + "unknown": "Ismeretlen hiba t\u00f6rt\u00e9nt." + }, + "error": { + "invalid_2fa": "\u00c9rv\u00e9nytelen K\u00e9tfaktoros hiteles\u00edt\u00e9s, pr\u00f3b\u00e1ld \u00fajra.", + "invalid_2fa_method": "\u00c9rv\u00e9nytelen 2FA M\u00f3dszer (Ellen\u0151rz\u00e9s a Telefonon).", + "invalid_login": "\u00c9rv\u00e9nytelen bejelentkez\u00e9s, pr\u00f3b\u00e1ld \u00fajra." + }, + "step": { + "2fa": { + "data": { + "2fa": "2FA Pin" + }, + "title": "K\u00e9tfaktoros Hiteles\u00edt\u00e9s" + }, + "user": { + "data": { + "email": "E-Mail C\u00edm", + "password": "Jelsz\u00f3" + }, + "title": "Google Hangouts Bejelentkez\u00e9s" + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/pt-BR.json b/homeassistant/components/hangouts/.translations/pt-BR.json index 41b097f3f8d..516229c3871 100644 --- a/homeassistant/components/hangouts/.translations/pt-BR.json +++ b/homeassistant/components/hangouts/.translations/pt-BR.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "Hangouts do Google j\u00e1 est\u00e1 configurado." + "already_configured": "Hangouts do Google j\u00e1 est\u00e1 configurado.", + "unknown": "Ocorreu um erro desconhecido." + }, + "error": { + "invalid_2fa": "Autentica\u00e7\u00e3o de 2 fatores inv\u00e1lida, por favor, tente novamente." }, "step": { "2fa": { diff --git a/homeassistant/components/homematicip_cloud/.translations/no.json b/homeassistant/components/homematicip_cloud/.translations/no.json index a310a918f64..730f00ae625 100644 --- a/homeassistant/components/homematicip_cloud/.translations/no.json +++ b/homeassistant/components/homematicip_cloud/.translations/no.json @@ -22,7 +22,7 @@ "title": "Velg HomematicIP tilgangspunkt" }, "link": { - "description": "Trykk p\u00e5 den bl\u00e5 knappen p\u00e5 tilgangspunktet og send knappen for \u00e5 registrere HomematicIP med Home Assistant. \n\n![Plassering av knapp p\u00e5 bridge](/static/images/config_flows/config_homematicip_cloud.png)", + "description": "Trykk p\u00e5 den bl\u00e5 knappen p\u00e5 tilgangspunktet og p\u00e5 send knappen for \u00e5 registrere HomematicIP med Home Assistant. \n\n![Plassering av knapp p\u00e5 bridge](/static/images/config_flows/config_homematicip_cloud.png)", "title": "Link tilgangspunkt" } }, diff --git a/homeassistant/components/nest/.translations/fr.json b/homeassistant/components/nest/.translations/fr.json index 62a4d7deec9..734e82dbcd0 100644 --- a/homeassistant/components/nest/.translations/fr.json +++ b/homeassistant/components/nest/.translations/fr.json @@ -2,6 +2,22 @@ "config": { "abort": { "already_setup": "Vous ne pouvez configurer qu'un seul compte Nest." - } + }, + "error": { + "internal_error": "Erreur interne lors de la validation du code", + "invalid_code": "Code invalide" + }, + "step": { + "init": { + "title": "Fournisseur d'authentification" + }, + "link": { + "data": { + "code": "Code PIN" + }, + "title": "Lier un compte Nest" + } + }, + "title": "Nest" } } \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.hu.json b/homeassistant/components/sensor/.translations/moon.hu.json new file mode 100644 index 00000000000..0fcd02a6961 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.hu.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Els\u0151 negyed", + "full_moon": "Telihold", + "last_quarter": "Utols\u00f3 negyed", + "new_moon": "\u00dajhold", + "waning_crescent": "Fogy\u00f3 Hold (sarl\u00f3)", + "waning_gibbous": "Fogy\u00f3 Hold", + "waxing_crescent": "N\u00f6v\u0151 Hold (sarl\u00f3)", + "waxing_gibbous": "N\u00f6v\u0151 Hold" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.no.json b/homeassistant/components/sensor/.translations/moon.no.json index 104412c90ba..19f9985accb 100644 --- a/homeassistant/components/sensor/.translations/moon.no.json +++ b/homeassistant/components/sensor/.translations/moon.no.json @@ -1,8 +1,8 @@ { "state": { - "first_quarter": "F\u00f8rste kvartdel", + "first_quarter": "F\u00f8rste kvarter", "full_moon": "Fullm\u00e5ne", - "last_quarter": "Siste kvartdel", + "last_quarter": "Siste kvarter", "new_moon": "Nym\u00e5ne", "waning_crescent": "Minkende halvm\u00e5ne", "waning_gibbous": "Minkende trekvartm\u00e5ne", diff --git a/homeassistant/components/sensor/.translations/moon.pt-BR.json b/homeassistant/components/sensor/.translations/moon.pt-BR.json index 57d3a3e95e4..93b17784a4e 100644 --- a/homeassistant/components/sensor/.translations/moon.pt-BR.json +++ b/homeassistant/components/sensor/.translations/moon.pt-BR.json @@ -1,7 +1,9 @@ { "state": { - "full_moon": "Cheia", - "new_moon": "Nova", + "first_quarter": "Quarto crescente", + "full_moon": "Lua cheia", + "last_quarter": "Quarto minguante", + "new_moon": "Lua Nova", "waning_crescent": "Minguante", "waning_gibbous": "Minguante gibosa", "waxing_crescent": "Crescente", diff --git a/homeassistant/components/sonos/.translations/fr.json b/homeassistant/components/sonos/.translations/fr.json new file mode 100644 index 00000000000..768a798e6d5 --- /dev/null +++ b/homeassistant/components/sonos/.translations/fr.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "no_devices_found": "Aucun p\u00e9riph\u00e9rique Sonos trouv\u00e9 sur le r\u00e9seau.", + "single_instance_allowed": "Seulement une seule configuration de Sonos est n\u00e9cessaire." + }, + "step": { + "confirm": { + "description": "Voulez-vous configurer Sonos?" + } + } + } +} \ No newline at end of file From b9c80a6bb36215e0a45aa2e9438a2b4f73085fbd Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 3 Sep 2018 13:21:37 +0200 Subject: [PATCH 032/357] Update translations --- .../components/auth/.translations/hu.json | 16 ++++++++++ .../components/auth/.translations/ko.json | 2 +- .../components/cast/.translations/fr.json | 6 ++-- .../components/deconz/.translations/no.json | 2 +- .../components/hangouts/.translations/fr.json | 7 +++-- .../components/hangouts/.translations/hu.json | 29 +++++++++++++++++++ .../hangouts/.translations/pt-BR.json | 6 +++- .../homematicip_cloud/.translations/no.json | 2 +- .../components/nest/.translations/fr.json | 18 +++++++++++- .../sensor/.translations/moon.hu.json | 12 ++++++++ .../sensor/.translations/moon.no.json | 4 +-- .../sensor/.translations/moon.pt-BR.json | 6 ++-- .../components/sonos/.translations/fr.json | 13 +++++++++ 13 files changed, 110 insertions(+), 13 deletions(-) create mode 100644 homeassistant/components/auth/.translations/hu.json create mode 100644 homeassistant/components/hangouts/.translations/hu.json create mode 100644 homeassistant/components/sensor/.translations/moon.hu.json create mode 100644 homeassistant/components/sonos/.translations/fr.json diff --git a/homeassistant/components/auth/.translations/hu.json b/homeassistant/components/auth/.translations/hu.json new file mode 100644 index 00000000000..4500098553e --- /dev/null +++ b/homeassistant/components/auth/.translations/hu.json @@ -0,0 +1,16 @@ +{ + "mfa_setup": { + "totp": { + "error": { + "invalid_code": "\u00c9rv\u00e9nytelen k\u00f3d, pr\u00f3b\u00e1ld \u00fajra. Ha ez a hiba folyamatosan el\u0151fordul, akkor gy\u0151z\u0151dj meg r\u00f3la, hogy a Home Assistant rendszered \u00f3r\u00e1ja pontosan j\u00e1r." + }, + "step": { + "init": { + "description": "Ahhoz, hogy haszn\u00e1lhasd a k\u00e9tfaktoros hiteles\u00edt\u00e9st id\u0151alap\u00fa egyszeri jelszavakkal, szkenneld be a QR k\u00f3dot a hiteles\u00edt\u00e9si applik\u00e1ci\u00f3ddal. Ha m\u00e9g nincsen, akkor a [Google Hiteles\u00edt\u0151](https://support.google.com/accounts/answer/1066447)t vagy az [Authy](https://authy.com/)-t aj\u00e1nljuk.\n\n{qr_code}\n\nA k\u00f3d beolvas\u00e1sa ut\u00e1n add meg a hat sz\u00e1mjegy\u0171 k\u00f3dot az applik\u00e1ci\u00f3b\u00f3l a telep\u00edt\u00e9s ellen\u0151rz\u00e9s\u00e9hez. Ha probl\u00e9m\u00e1ba \u00fctk\u00f6z\u00f6l a QR k\u00f3d beolvas\u00e1s\u00e1n\u00e1l, akkor ind\u00edts egy k\u00e9zi be\u00e1ll\u00edt\u00e1st a **`{code}`** k\u00f3ddal.", + "title": "K\u00e9tfaktoros hiteles\u00edt\u00e9s be\u00e1ll\u00edt\u00e1sa TOTP haszn\u00e1lat\u00e1val" + } + }, + "title": "TOTP" + } + } +} \ No newline at end of file diff --git a/homeassistant/components/auth/.translations/ko.json b/homeassistant/components/auth/.translations/ko.json index 4eb4783edd9..17fb5c56f57 100644 --- a/homeassistant/components/auth/.translations/ko.json +++ b/homeassistant/components/auth/.translations/ko.json @@ -6,7 +6,7 @@ }, "step": { "init": { - "description": "\uc2dc\uac04 \uae30\ubc18\uc758 \uc77c\ud68c\uc6a9 \ube44\ubc00\ubc88\ud638\ub97c \uc0ac\uc6a9\ud558\ub294 2\ub2e8\uacc4 \uc778\uc99d\uc744 \ud558\ub824\uba74 \uc778\uc99d\uc6a9 \uc571\uc744 \uc774\uc6a9\ud574\uc11c QR \ucf54\ub4dc\ub97c \uc2a4\uce94\ud574 \uc8fc\uc138\uc694. \uc778\uc99d\uc6a9 \uc571\uc740 [Google Authenticator](https://support.google.com/accounts/answer/1066447) \ub610\ub294 [Authy](https://authy.com/) \ub97c \ucd94\ucc9c\ub4dc\ub9bd\ub2c8\ub2e4.\n\n{qr_code}\n\n\uc2a4\uce94 \ud6c4\uc5d0 \uc0dd\uc131\ub41c 6\uc790\ub9ac \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc11c \uc124\uc815\uc744 \ud655\uc778\ud558\uc138\uc694. QR \ucf54\ub4dc \uc2a4\uce94\uc5d0 \ubb38\uc81c\uac00 \uc788\ub2e4\uba74, **`{code}`** \ucf54\ub4dc\ub85c \uc9c1\uc811 \uc124\uc815\ud574\ubcf4\uc138\uc694.", + "description": "\uc2dc\uac04 \uae30\ubc18\uc758 \uc77c\ud68c\uc6a9 \ube44\ubc00\ubc88\ud638\ub97c \uc0ac\uc6a9\ud558\ub294 2\ub2e8\uacc4 \uc778\uc99d\uc744 \ud558\ub824\uba74 \uc778\uc99d\uc6a9 \uc571\uc744 \uc774\uc6a9\ud574\uc11c QR \ucf54\ub4dc\ub97c \uc2a4\uce94\ud574 \uc8fc\uc138\uc694. \uc778\uc99d\uc6a9 \uc571\uc740 [Google OTP](https://support.google.com/accounts/answer/1066447) \ub610\ub294 [Authy](https://authy.com/) \ub97c \ucd94\ucc9c\ub4dc\ub9bd\ub2c8\ub2e4.\n\n{qr_code}\n\n\uc2a4\uce94 \ud6c4\uc5d0 \uc0dd\uc131\ub41c 6\uc790\ub9ac \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc11c \uc124\uc815\uc744 \ud655\uc778\ud558\uc138\uc694. QR \ucf54\ub4dc \uc2a4\uce94\uc5d0 \ubb38\uc81c\uac00 \uc788\ub2e4\uba74, **`{code}`** \ucf54\ub4dc\ub85c \uc9c1\uc811 \uc124\uc815\ud574\ubcf4\uc138\uc694.", "title": "TOTP \ub97c \uc0ac\uc6a9\ud558\uc5ec 2 \ub2e8\uacc4 \uc778\uc99d \uad6c\uc131" } }, diff --git a/homeassistant/components/cast/.translations/fr.json b/homeassistant/components/cast/.translations/fr.json index acacddf2187..d3b95121de6 100644 --- a/homeassistant/components/cast/.translations/fr.json +++ b/homeassistant/components/cast/.translations/fr.json @@ -6,8 +6,10 @@ }, "step": { "confirm": { - "description": "Voulez-vous configurer Google Cast?" + "description": "Voulez-vous configurer Google Cast?", + "title": "Google Cast" } - } + }, + "title": "Google Cast" } } \ No newline at end of file diff --git a/homeassistant/components/deconz/.translations/no.json b/homeassistant/components/deconz/.translations/no.json index 55518b7da53..27868814eab 100644 --- a/homeassistant/components/deconz/.translations/no.json +++ b/homeassistant/components/deconz/.translations/no.json @@ -28,6 +28,6 @@ "title": "Ekstra konfigurasjonsalternativer for deCONZ" } }, - "title": "deCONZ" + "title": "deCONZ Zigbee gateway" } } \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/fr.json b/homeassistant/components/hangouts/.translations/fr.json index c92d478c454..53759f9b534 100644 --- a/homeassistant/components/hangouts/.translations/fr.json +++ b/homeassistant/components/hangouts/.translations/fr.json @@ -13,9 +13,12 @@ }, "user": { "data": { + "email": "Adresse e-mail", "password": "Mot de passe" - } + }, + "title": "Connexion \u00e0 Google Hangouts" } - } + }, + "title": "Google Hangouts" } } \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/hu.json b/homeassistant/components/hangouts/.translations/hu.json new file mode 100644 index 00000000000..2631843c784 --- /dev/null +++ b/homeassistant/components/hangouts/.translations/hu.json @@ -0,0 +1,29 @@ +{ + "config": { + "abort": { + "already_configured": "A Google Hangouts m\u00e1r konfigur\u00e1lva van", + "unknown": "Ismeretlen hiba t\u00f6rt\u00e9nt." + }, + "error": { + "invalid_2fa": "\u00c9rv\u00e9nytelen K\u00e9tfaktoros hiteles\u00edt\u00e9s, pr\u00f3b\u00e1ld \u00fajra.", + "invalid_2fa_method": "\u00c9rv\u00e9nytelen 2FA M\u00f3dszer (Ellen\u0151rz\u00e9s a Telefonon).", + "invalid_login": "\u00c9rv\u00e9nytelen bejelentkez\u00e9s, pr\u00f3b\u00e1ld \u00fajra." + }, + "step": { + "2fa": { + "data": { + "2fa": "2FA Pin" + }, + "title": "K\u00e9tfaktoros Hiteles\u00edt\u00e9s" + }, + "user": { + "data": { + "email": "E-Mail C\u00edm", + "password": "Jelsz\u00f3" + }, + "title": "Google Hangouts Bejelentkez\u00e9s" + } + }, + "title": "Google Hangouts" + } +} \ No newline at end of file diff --git a/homeassistant/components/hangouts/.translations/pt-BR.json b/homeassistant/components/hangouts/.translations/pt-BR.json index 41b097f3f8d..516229c3871 100644 --- a/homeassistant/components/hangouts/.translations/pt-BR.json +++ b/homeassistant/components/hangouts/.translations/pt-BR.json @@ -1,7 +1,11 @@ { "config": { "abort": { - "already_configured": "Hangouts do Google j\u00e1 est\u00e1 configurado." + "already_configured": "Hangouts do Google j\u00e1 est\u00e1 configurado.", + "unknown": "Ocorreu um erro desconhecido." + }, + "error": { + "invalid_2fa": "Autentica\u00e7\u00e3o de 2 fatores inv\u00e1lida, por favor, tente novamente." }, "step": { "2fa": { diff --git a/homeassistant/components/homematicip_cloud/.translations/no.json b/homeassistant/components/homematicip_cloud/.translations/no.json index a310a918f64..730f00ae625 100644 --- a/homeassistant/components/homematicip_cloud/.translations/no.json +++ b/homeassistant/components/homematicip_cloud/.translations/no.json @@ -22,7 +22,7 @@ "title": "Velg HomematicIP tilgangspunkt" }, "link": { - "description": "Trykk p\u00e5 den bl\u00e5 knappen p\u00e5 tilgangspunktet og send knappen for \u00e5 registrere HomematicIP med Home Assistant. \n\n![Plassering av knapp p\u00e5 bridge](/static/images/config_flows/config_homematicip_cloud.png)", + "description": "Trykk p\u00e5 den bl\u00e5 knappen p\u00e5 tilgangspunktet og p\u00e5 send knappen for \u00e5 registrere HomematicIP med Home Assistant. \n\n![Plassering av knapp p\u00e5 bridge](/static/images/config_flows/config_homematicip_cloud.png)", "title": "Link tilgangspunkt" } }, diff --git a/homeassistant/components/nest/.translations/fr.json b/homeassistant/components/nest/.translations/fr.json index 62a4d7deec9..734e82dbcd0 100644 --- a/homeassistant/components/nest/.translations/fr.json +++ b/homeassistant/components/nest/.translations/fr.json @@ -2,6 +2,22 @@ "config": { "abort": { "already_setup": "Vous ne pouvez configurer qu'un seul compte Nest." - } + }, + "error": { + "internal_error": "Erreur interne lors de la validation du code", + "invalid_code": "Code invalide" + }, + "step": { + "init": { + "title": "Fournisseur d'authentification" + }, + "link": { + "data": { + "code": "Code PIN" + }, + "title": "Lier un compte Nest" + } + }, + "title": "Nest" } } \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.hu.json b/homeassistant/components/sensor/.translations/moon.hu.json new file mode 100644 index 00000000000..0fcd02a6961 --- /dev/null +++ b/homeassistant/components/sensor/.translations/moon.hu.json @@ -0,0 +1,12 @@ +{ + "state": { + "first_quarter": "Els\u0151 negyed", + "full_moon": "Telihold", + "last_quarter": "Utols\u00f3 negyed", + "new_moon": "\u00dajhold", + "waning_crescent": "Fogy\u00f3 Hold (sarl\u00f3)", + "waning_gibbous": "Fogy\u00f3 Hold", + "waxing_crescent": "N\u00f6v\u0151 Hold (sarl\u00f3)", + "waxing_gibbous": "N\u00f6v\u0151 Hold" + } +} \ No newline at end of file diff --git a/homeassistant/components/sensor/.translations/moon.no.json b/homeassistant/components/sensor/.translations/moon.no.json index 104412c90ba..19f9985accb 100644 --- a/homeassistant/components/sensor/.translations/moon.no.json +++ b/homeassistant/components/sensor/.translations/moon.no.json @@ -1,8 +1,8 @@ { "state": { - "first_quarter": "F\u00f8rste kvartdel", + "first_quarter": "F\u00f8rste kvarter", "full_moon": "Fullm\u00e5ne", - "last_quarter": "Siste kvartdel", + "last_quarter": "Siste kvarter", "new_moon": "Nym\u00e5ne", "waning_crescent": "Minkende halvm\u00e5ne", "waning_gibbous": "Minkende trekvartm\u00e5ne", diff --git a/homeassistant/components/sensor/.translations/moon.pt-BR.json b/homeassistant/components/sensor/.translations/moon.pt-BR.json index 57d3a3e95e4..93b17784a4e 100644 --- a/homeassistant/components/sensor/.translations/moon.pt-BR.json +++ b/homeassistant/components/sensor/.translations/moon.pt-BR.json @@ -1,7 +1,9 @@ { "state": { - "full_moon": "Cheia", - "new_moon": "Nova", + "first_quarter": "Quarto crescente", + "full_moon": "Lua cheia", + "last_quarter": "Quarto minguante", + "new_moon": "Lua Nova", "waning_crescent": "Minguante", "waning_gibbous": "Minguante gibosa", "waxing_crescent": "Crescente", diff --git a/homeassistant/components/sonos/.translations/fr.json b/homeassistant/components/sonos/.translations/fr.json new file mode 100644 index 00000000000..768a798e6d5 --- /dev/null +++ b/homeassistant/components/sonos/.translations/fr.json @@ -0,0 +1,13 @@ +{ + "config": { + "abort": { + "no_devices_found": "Aucun p\u00e9riph\u00e9rique Sonos trouv\u00e9 sur le r\u00e9seau.", + "single_instance_allowed": "Seulement une seule configuration de Sonos est n\u00e9cessaire." + }, + "step": { + "confirm": { + "description": "Voulez-vous configurer Sonos?" + } + } + } +} \ No newline at end of file From 46734b8409a1f13186aa44cfae43e144d93edb3b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 3 Sep 2018 13:22:49 +0200 Subject: [PATCH 033/357] Bumped version to 0.77.3 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index fac257fbaed..d2066e0557d 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 77 -PATCH_VERSION = '2' +PATCH_VERSION = '3' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 00cba29ae189e3846a39beef7f830d909f4c6cb8 Mon Sep 17 00:00:00 2001 From: Pawel Date: Mon, 3 Sep 2018 21:40:04 +0200 Subject: [PATCH 034/357] Support for playing radio preset by Onkyo media_player (#16258) * Added support to play radio preset by play media * Switch radio station by preset for Onkyo * added SUPPORT_PLAY_MEDIA --- homeassistant/components/media_player/onkyo.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/media_player/onkyo.py b/homeassistant/components/media_player/onkyo.py index af9a6ef54ce..00df456804f 100644 --- a/homeassistant/components/media_player/onkyo.py +++ b/homeassistant/components/media_player/onkyo.py @@ -14,7 +14,7 @@ import voluptuous as vol from homeassistant.components.media_player import ( SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_STEP, SUPPORT_SELECT_SOURCE, SUPPORT_PLAY, - MediaPlayerDevice, PLATFORM_SCHEMA) + SUPPORT_PLAY_MEDIA, MediaPlayerDevice, PLATFORM_SCHEMA) from homeassistant.const import (STATE_OFF, STATE_ON, CONF_HOST, CONF_NAME) import homeassistant.helpers.config_validation as cv @@ -30,17 +30,19 @@ SUPPORTED_MAX_VOLUME = 80 SUPPORT_ONKYO = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ SUPPORT_VOLUME_STEP | SUPPORT_TURN_ON | SUPPORT_TURN_OFF | \ - SUPPORT_SELECT_SOURCE | SUPPORT_PLAY + SUPPORT_SELECT_SOURCE | SUPPORT_PLAY | SUPPORT_PLAY_MEDIA SUPPORT_ONKYO_WO_VOLUME = SUPPORT_TURN_ON | SUPPORT_TURN_OFF | \ - SUPPORT_SELECT_SOURCE | SUPPORT_PLAY + SUPPORT_SELECT_SOURCE | SUPPORT_PLAY | SUPPORT_PLAY_MEDIA KNOWN_HOSTS = [] # type: List[str] DEFAULT_SOURCES = {'tv': 'TV', 'bd': 'Bluray', 'game': 'Game', 'aux1': 'Aux1', 'video1': 'Video 1', 'video2': 'Video 2', 'video3': 'Video 3', 'video4': 'Video 4', 'video5': 'Video 5', 'video6': 'Video 6', - 'video7': 'Video 7'} + 'video7': 'Video 7', 'fm': 'Radio'} + +DEFAULT_PLAYABLE_SOURCES = ("fm", "am", "tuner") PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HOST): cv.string, @@ -266,6 +268,13 @@ class OnkyoDevice(MediaPlayerDevice): source = self._reverse_mapping[source] self.command('input-selector {}'.format(source)) + def play_media(self, media_type, media_id, **kwargs): + """Play radio station by preset number.""" + source = self._reverse_mapping[self._current_source] + if (media_type.lower() == 'radio' and + source in DEFAULT_PLAYABLE_SOURCES): + self.command('preset {}'.format(media_id)) + class OnkyoDeviceZone(OnkyoDevice): """Representation of an Onkyo device's extra zone.""" From 2252f4a25797ba423bd452682c8b2d8048e26fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Tue, 4 Sep 2018 01:11:40 +0200 Subject: [PATCH 035/357] Bug fix for Tibber (#16397) --- homeassistant/components/sensor/tibber.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensor/tibber.py b/homeassistant/components/sensor/tibber.py index 3670a5a59bd..ebc38fcb739 100644 --- a/homeassistant/components/sensor/tibber.py +++ b/homeassistant/components/sensor/tibber.py @@ -151,7 +151,7 @@ class TibberSensor(Entity): if now.date() == price_time.date(): max_price = max(max_price, price_total) min_price = min(min_price, price_total) - self._state = state - self._device_state_attributes['max_price'] = max_price - self._device_state_attributes['min_price'] = min_price + self._state = state + self._device_state_attributes['max_price'] = max_price + self._device_state_attributes['min_price'] = min_price return state is not None From ba63a6abc0e46c632041823b7b7900ab13b2611f Mon Sep 17 00:00:00 2001 From: Russell Cloran Date: Mon, 3 Sep 2018 22:46:27 -0700 Subject: [PATCH 036/357] zha: Bump to zigpy 0.2.0/bellows 0.7.0 (#16404) --- homeassistant/components/zha/__init__.py | 4 ++-- requirements_all.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index f17e7f02344..7aec4333ea8 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -16,8 +16,8 @@ from homeassistant.helpers import discovery, entity from homeassistant.util import slugify REQUIREMENTS = [ - 'bellows==0.6.0', - 'zigpy==0.1.0', + 'bellows==0.7.0', + 'zigpy==0.2.0', 'zigpy-xbee==0.1.1', ] diff --git a/requirements_all.txt b/requirements_all.txt index 6223f9cc51c..d8d73cc36a1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -168,7 +168,7 @@ batinfo==0.4.2 beautifulsoup4==4.6.3 # homeassistant.components.zha -bellows==0.6.0 +bellows==0.7.0 # homeassistant.components.bmw_connected_drive bimmer_connected==0.5.1 @@ -1530,4 +1530,4 @@ ziggo-mediabox-xl==1.0.0 zigpy-xbee==0.1.1 # homeassistant.components.zha -zigpy==0.1.0 +zigpy==0.2.0 From a4aa30fc73a625a5567b68bc6d06733694b8efcd Mon Sep 17 00:00:00 2001 From: Rene Nulsch <33263735+ReneNulschDE@users.noreply.github.com> Date: Tue, 4 Sep 2018 08:46:04 +0200 Subject: [PATCH 037/357] Fix SystemMonitor IP address sensor (#16394) --- homeassistant/components/sensor/systemmonitor.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/sensor/systemmonitor.py b/homeassistant/components/sensor/systemmonitor.py index aa448ddf56e..de8e9783f92 100644 --- a/homeassistant/components/sensor/systemmonitor.py +++ b/homeassistant/components/sensor/systemmonitor.py @@ -6,6 +6,7 @@ https://home-assistant.io/components/sensor.systemmonitor/ """ import logging import os +import socket import voluptuous as vol @@ -61,9 +62,9 @@ IO_COUNTER = { 'packets_in': 3, } -IF_ADDRS = { - 'ipv4_address': 0, - 'ipv6_address': 1, +IF_ADDRS_FAMILY = { + 'ipv4_address': socket.AF_INET, + 'ipv6_address': socket.AF_INET6, } @@ -165,7 +166,9 @@ class SystemMonitorSensor(Entity): elif self.type == 'ipv4_address' or self.type == 'ipv6_address': addresses = psutil.net_if_addrs() if self.argument in addresses: - self._state = addresses[self.argument][IF_ADDRS[self.type]][1] + for addr in addresses[self.argument]: + if addr.family == IF_ADDRS_FAMILY[self.type]: + self._state = addr.address else: self._state = None elif self.type == 'last_boot': From 7ea482cb1d6b7cb247dc105fd780416e8fd0ab51 Mon Sep 17 00:00:00 2001 From: 9R Date: Tue, 4 Sep 2018 08:48:03 +0200 Subject: [PATCH 038/357] add ExpressBus icon key to sensor.mvg (#16387) --- homeassistant/components/sensor/mvglive.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/mvglive.py b/homeassistant/components/sensor/mvglive.py index a7a4b592664..8634e4f4570 100644 --- a/homeassistant/components/sensor/mvglive.py +++ b/homeassistant/components/sensor/mvglive.py @@ -31,12 +31,13 @@ CONF_PRODUCTS = 'products' CONF_TIMEOFFSET = 'timeoffset' CONF_NUMBER = 'number' -DEFAULT_PRODUCT = ['U-Bahn', 'Tram', 'Bus', 'S-Bahn'] +DEFAULT_PRODUCT = ['U-Bahn', 'Tram', 'Bus', 'ExpressBus', 'S-Bahn'] ICONS = { 'U-Bahn': 'mdi:subway', 'Tram': 'mdi:tram', 'Bus': 'mdi:bus', + 'ExpressBus': 'mdi:bus', 'S-Bahn': 'mdi:train', 'SEV': 'mdi:checkbox-blank-circle-outline', '-': 'mdi:clock' From 7a6facc875ac0f3af33450d77880777ffcb3816d Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Tue, 4 Sep 2018 09:00:14 +0200 Subject: [PATCH 039/357] Device and entity registry remove config entry on unload (#16247) * Test * Ability to remove device * Don't remove devices, instead remove config entry from device and entity registries * Remove print * Remove is not the same as unload * Add tests * Fix hound comment --- homeassistant/config_entries.py | 8 +++++++ homeassistant/helpers/device_registry.py | 10 ++++++++- homeassistant/helpers/entity_registry.py | 10 ++++++++- tests/helpers/test_device_registry.py | 27 ++++++++++++++++++++++++ tests/helpers/test_entity_registry.py | 9 ++++++++ 5 files changed, 62 insertions(+), 2 deletions(-) diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 8db09cdb8da..6eae9e13030 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -325,6 +325,14 @@ class ConfigEntries: unloaded = await entry.async_unload(self.hass) + device_registry = await \ + self.hass.helpers.device_registry.async_get_registry() + device_registry.async_clear_config_entry(entry_id) + + entity_registry = await \ + self.hass.helpers.entity_registry.async_get_registry() + entity_registry.async_clear_config_entry(entry_id) + return { 'require_restart': not unloaded } diff --git a/homeassistant/helpers/device_registry.py b/homeassistant/helpers/device_registry.py index 504448b948d..e6ff45af2fe 100644 --- a/homeassistant/helpers/device_registry.py +++ b/homeassistant/helpers/device_registry.py @@ -45,7 +45,7 @@ class DeviceRegistry: self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) @callback - def async_get_device(self, identifiers: str, connections: tuple): + def async_get_device(self, identifiers: set, connections: set): """Check if device is registered.""" for device in self.devices.values(): if any(iden in device.identifiers for iden in identifiers) or \ @@ -127,6 +127,14 @@ class DeviceRegistry: return data + @callback + def async_clear_config_entry(self, config_entry): + """Clear config entry from registry entries.""" + for device in self.devices.values(): + if config_entry in device.config_entries: + device.config_entries.remove(config_entry) + self.async_schedule_save() + @bind_hass async def async_get_registry(hass) -> DeviceRegistry: diff --git a/homeassistant/helpers/entity_registry.py b/homeassistant/helpers/entity_registry.py index 804ee4235d0..da3645a96fe 100644 --- a/homeassistant/helpers/entity_registry.py +++ b/homeassistant/helpers/entity_registry.py @@ -31,7 +31,7 @@ STORAGE_VERSION = 1 STORAGE_KEY = 'core.entity_registry' -@attr.s(slots=True, frozen=True) +@attr.s(slots=True) class RegistryEntry: """Entity Registry Entry.""" @@ -250,6 +250,14 @@ class EntityRegistry: return data + @callback + def async_clear_config_entry(self, config_entry): + """Clear config entry from registry entries.""" + for entry in self.entities.values(): + if config_entry == entry.config_entry_id: + entry.config_entry_id = None + self.async_schedule_save() + @bind_hass async def async_get_registry(hass) -> EntityRegistry: diff --git a/tests/helpers/test_device_registry.py b/tests/helpers/test_device_registry.py index 84ad54f7b82..a9132529bc3 100644 --- a/tests/helpers/test_device_registry.py +++ b/tests/helpers/test_device_registry.py @@ -138,3 +138,30 @@ async def test_loading_from_storage(hass, hass_storage): manufacturer='manufacturer', model='model') assert entry.id == 'abcdefghijklm' assert isinstance(entry.config_entries, set) + + +async def test_removing_config_entries(registry): + """Make sure we do not get duplicate entries.""" + entry = registry.async_get_or_create( + config_entry='123', + connections={('ethernet', '12:34:56:78:90:AB:CD:EF')}, + identifiers={('bridgeid', '0123')}, + manufacturer='manufacturer', model='model') + entry2 = registry.async_get_or_create( + config_entry='456', + connections={('ethernet', '12:34:56:78:90:AB:CD:EF')}, + identifiers={('bridgeid', '0123')}, + manufacturer='manufacturer', model='model') + entry3 = registry.async_get_or_create( + config_entry='123', + connections={('ethernet', '34:56:78:90:AB:CD:EF:12')}, + identifiers={('bridgeid', '4567')}, + manufacturer='manufacturer', model='model') + + assert len(registry.devices) == 2 + assert entry is entry2 + assert entry is not entry3 + assert entry.config_entries == {'123', '456'} + registry.async_clear_config_entry('123') + assert entry.config_entries == {'456'} + assert entry3.config_entries == set() diff --git a/tests/helpers/test_entity_registry.py b/tests/helpers/test_entity_registry.py index d0c088a6f69..bb28287ddd8 100644 --- a/tests/helpers/test_entity_registry.py +++ b/tests/helpers/test_entity_registry.py @@ -186,6 +186,15 @@ async def test_updating_config_entry_id(registry): assert entry2.config_entry_id == 'mock-id-2' +async def test_removing_config_entry_id(registry): + """Test that we update config entry id in registry.""" + entry = registry.async_get_or_create( + 'light', 'hue', '5678', config_entry_id='mock-id-1') + assert entry.config_entry_id == 'mock-id-1' + registry.async_clear_config_entry('mock-id-1') + assert entry.config_entry_id is None + + async def test_migration(hass): """Test migration from old data to new.""" old_conf = { From f96aee2832b8834e48c24ccd54c3d0218614a641 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Tue, 4 Sep 2018 01:22:44 -0600 Subject: [PATCH 040/357] Add config flow for OpenUV (#16159) * OpenUV config flow in place * Test folder in place * Owner-requested comments * Tests * More tests * Owner-requested changes (part 1 of 2) * Updated requirements * Owner-requested changes (2 of 2) * Removed unnecessary import * Bumping Travis * Updated requirements * More requirements * Updated tests * Owner-requested changes * Hound * Updated docstring --- .coveragerc | 8 +- .../components/binary_sensor/openuv.py | 27 ++-- .../components/openuv/.translations/en.json | 20 +++ .../{openuv.py => openuv/__init__.py} | 128 +++++++++++++----- .../components/openuv/config_flow.py | 73 ++++++++++ homeassistant/components/openuv/const.py | 3 + homeassistant/components/openuv/strings.json | 20 +++ homeassistant/components/sensor/openuv.py | 34 +++-- homeassistant/config_entries.py | 1 + requirements_all.txt | 2 +- requirements_test_all.txt | 3 + script/gen_requirements_all.py | 1 + tests/components/openuv/__init__.py | 1 + tests/components/openuv/test_config_flow.py | 93 +++++++++++++ 14 files changed, 348 insertions(+), 66 deletions(-) create mode 100644 homeassistant/components/openuv/.translations/en.json rename homeassistant/components/{openuv.py => openuv/__init__.py} (57%) create mode 100644 homeassistant/components/openuv/config_flow.py create mode 100644 homeassistant/components/openuv/const.py create mode 100644 homeassistant/components/openuv/strings.json create mode 100644 tests/components/openuv/__init__.py create mode 100644 tests/components/openuv/test_config_flow.py diff --git a/.coveragerc b/.coveragerc index 39c31e4e40b..bd531e62f72 100644 --- a/.coveragerc +++ b/.coveragerc @@ -123,7 +123,7 @@ omit = homeassistant/components/hangouts/const.py homeassistant/components/hangouts/hangouts_bot.py homeassistant/components/hangouts/hangups_utils.py - homeassistant/components/*/hangouts.py + homeassistant/components/*/hangouts.py homeassistant/components/hdmi_cec.py homeassistant/components/*/hdmi_cec.py @@ -145,12 +145,12 @@ omit = homeassistant/components/ihc/* homeassistant/components/*/ihc.py - + homeassistant/components/insteon/* homeassistant/components/*/insteon.py homeassistant/components/insteon_local.py - + homeassistant/components/insteon_plm.py homeassistant/components/ios.py @@ -228,7 +228,7 @@ omit = homeassistant/components/opencv.py homeassistant/components/*/opencv.py - homeassistant/components/openuv.py + homeassistant/components/openuv/__init__.py homeassistant/components/*/openuv.py homeassistant/components/pilight.py diff --git a/homeassistant/components/binary_sensor/openuv.py b/homeassistant/components/binary_sensor/openuv.py index 0b299529a46..c7c27d73ee4 100644 --- a/homeassistant/components/binary_sensor/openuv.py +++ b/homeassistant/components/binary_sensor/openuv.py @@ -7,12 +7,11 @@ https://home-assistant.io/components/binary_sensor.openuv/ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.const import CONF_MONITORED_CONDITIONS from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.components.openuv import ( - BINARY_SENSORS, DATA_PROTECTION_WINDOW, DOMAIN, TOPIC_UPDATE, - TYPE_PROTECTION_WINDOW, OpenUvEntity) + BINARY_SENSORS, DATA_OPENUV_CLIENT, DATA_PROTECTION_WINDOW, DOMAIN, + TOPIC_UPDATE, TYPE_PROTECTION_WINDOW, OpenUvEntity) from homeassistant.util.dt import as_local, parse_datetime, utcnow DEPENDENCIES = ['openuv'] @@ -26,17 +25,20 @@ ATTR_PROTECTION_WINDOW_ENDING_UV = 'end_uv' async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): - """Set up the OpenUV binary sensor platform.""" - if discovery_info is None: - return + """Set up an OpenUV sensor based on existing config.""" + pass - openuv = hass.data[DOMAIN] + +async def async_setup_entry(hass, entry, async_add_entities): + """Set up an OpenUV sensor based on a config entry.""" + openuv = hass.data[DOMAIN][DATA_OPENUV_CLIENT][entry.entry_id] binary_sensors = [] - for sensor_type in discovery_info[CONF_MONITORED_CONDITIONS]: + for sensor_type in openuv.binary_sensor_conditions: name, icon = BINARY_SENSORS[sensor_type] binary_sensors.append( - OpenUvBinarySensor(openuv, sensor_type, name, icon)) + OpenUvBinarySensor( + openuv, sensor_type, name, icon, entry.entry_id)) async_add_entities(binary_sensors, True) @@ -44,14 +46,16 @@ async def async_setup_platform( class OpenUvBinarySensor(OpenUvEntity, BinarySensorDevice): """Define a binary sensor for OpenUV.""" - def __init__(self, openuv, sensor_type, name, icon): + def __init__(self, openuv, sensor_type, name, icon, entry_id): """Initialize the sensor.""" super().__init__(openuv) + self._entry_id = entry_id self._icon = icon self._latitude = openuv.client.latitude self._longitude = openuv.client.longitude self._name = name + self._dispatch_remove = None self._sensor_type = sensor_type self._state = None @@ -83,8 +87,9 @@ class OpenUvBinarySensor(OpenUvEntity, BinarySensorDevice): async def async_added_to_hass(self): """Register callbacks.""" - async_dispatcher_connect( + self._dispatch_remove = async_dispatcher_connect( self.hass, TOPIC_UPDATE, self._update_data) + self.async_on_remove(self._dispatch_remove) async def async_update(self): """Update the state.""" diff --git a/homeassistant/components/openuv/.translations/en.json b/homeassistant/components/openuv/.translations/en.json new file mode 100644 index 00000000000..df0232d01fc --- /dev/null +++ b/homeassistant/components/openuv/.translations/en.json @@ -0,0 +1,20 @@ +{ + "config": { + "error": { + "identifier_exists": "Coordinates already registered", + "invalid_api_key": "Invalid API key" + }, + "step": { + "user": { + "data": { + "api_key": "OpenUV API Key", + "elevation": "Elevation", + "latitude": "Latitude", + "longitude": "Longitude" + }, + "title": "Fill in your information" + } + }, + "title": "OpenUV" + } +} \ No newline at end of file diff --git a/homeassistant/components/openuv.py b/homeassistant/components/openuv/__init__.py similarity index 57% rename from homeassistant/components/openuv.py rename to homeassistant/components/openuv/__init__.py index d696f0e5100..bfd90b4a574 100644 --- a/homeassistant/components/openuv.py +++ b/homeassistant/components/openuv/__init__.py @@ -1,5 +1,5 @@ """ -Support for data from openuv.io. +Support for UV data from openuv.io. For more details about this component, please refer to the documentation at https://home-assistant.io/components/openuv/ @@ -9,21 +9,24 @@ from datetime import timedelta import voluptuous as vol +from homeassistant.config_entries import SOURCE_IMPORT from homeassistant.const import ( ATTR_ATTRIBUTION, CONF_API_KEY, CONF_BINARY_SENSORS, CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE, CONF_MONITORED_CONDITIONS, CONF_SCAN_INTERVAL, CONF_SENSORS) -from homeassistant.helpers import ( - aiohttp_client, config_validation as cv, discovery) +from homeassistant.helpers import aiohttp_client, config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity import Entity from homeassistant.helpers.event import async_track_time_interval -REQUIREMENTS = ['pyopenuv==1.0.1'] +from .config_flow import configured_instances +from .const import DOMAIN + +REQUIREMENTS = ['pyopenuv==1.0.4'] _LOGGER = logging.getLogger(__name__) -DOMAIN = 'openuv' - +DATA_OPENUV_CLIENT = 'data_client' +DATA_OPENUV_LISTENER = 'data_listener' DATA_PROTECTION_WINDOW = 'protection_window' DATA_UV = 'uv' @@ -82,39 +85,77 @@ SENSOR_SCHEMA = vol.Schema({ }) CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({ - vol.Required(CONF_API_KEY): cv.string, - vol.Optional(CONF_ELEVATION): float, - vol.Optional(CONF_LATITUDE): cv.latitude, - vol.Optional(CONF_LONGITUDE): cv.longitude, - vol.Optional(CONF_BINARY_SENSORS, default={}): BINARY_SENSOR_SCHEMA, - vol.Optional(CONF_SENSORS, default={}): SENSOR_SCHEMA, - vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): - cv.time_period, - }) + DOMAIN: + vol.Schema({ + vol.Required(CONF_API_KEY): cv.string, + vol.Optional(CONF_ELEVATION): float, + vol.Optional(CONF_LATITUDE): cv.latitude, + vol.Optional(CONF_LONGITUDE): cv.longitude, + vol.Optional(CONF_BINARY_SENSORS, default={}): + BINARY_SENSOR_SCHEMA, + vol.Optional(CONF_SENSORS, default={}): SENSOR_SCHEMA, + vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): + cv.time_period, + }) }, extra=vol.ALLOW_EXTRA) async def async_setup(hass, config): """Set up the OpenUV component.""" - from pyopenuv import Client - from pyopenuv.errors import OpenUvError + hass.data[DOMAIN] = {} + hass.data[DOMAIN][DATA_OPENUV_CLIENT] = {} + hass.data[DOMAIN][DATA_OPENUV_LISTENER] = {} + + if DOMAIN not in config: + return True conf = config[DOMAIN] - api_key = conf[CONF_API_KEY] - elevation = conf.get(CONF_ELEVATION, hass.config.elevation) latitude = conf.get(CONF_LATITUDE, hass.config.latitude) longitude = conf.get(CONF_LONGITUDE, hass.config.longitude) + elevation = conf.get(CONF_ELEVATION, hass.config.elevation) + + identifier = '{0}, {1}'.format(latitude, longitude) + + if identifier not in configured_instances(hass): + hass.async_add_job( + hass.config_entries.flow.async_init( + DOMAIN, + context={'source': SOURCE_IMPORT}, + data={ + CONF_API_KEY: conf[CONF_API_KEY], + CONF_LATITUDE: latitude, + CONF_LONGITUDE: longitude, + CONF_ELEVATION: elevation, + CONF_BINARY_SENSORS: conf[CONF_BINARY_SENSORS], + CONF_SENSORS: conf[CONF_SENSORS], + })) + + hass.data[DOMAIN][CONF_SCAN_INTERVAL] = conf[CONF_SCAN_INTERVAL] + + return True + + +async def async_setup_entry(hass, config_entry): + """Set up OpenUV as config entry.""" + from pyopenuv import Client + from pyopenuv.errors import OpenUvError try: websession = aiohttp_client.async_get_clientsession(hass) openuv = OpenUV( Client( - api_key, latitude, longitude, websession, altitude=elevation), - conf[CONF_BINARY_SENSORS][CONF_MONITORED_CONDITIONS] + - conf[CONF_SENSORS][CONF_MONITORED_CONDITIONS]) + config_entry.data[CONF_API_KEY], + config_entry.data.get(CONF_LATITUDE, hass.config.latitude), + config_entry.data.get(CONF_LONGITUDE, hass.config.longitude), + websession, + altitude=config_entry.data.get( + CONF_ELEVATION, hass.config.elevation)), + config_entry.data.get(CONF_BINARY_SENSORS, {}).get( + CONF_MONITORED_CONDITIONS, list(BINARY_SENSORS)), + config_entry.data.get(CONF_SENSORS, {}).get( + CONF_MONITORED_CONDITIONS, list(SENSORS))) await openuv.async_update() - hass.data[DOMAIN] = openuv + hass.data[DOMAIN][DATA_OPENUV_CLIENT][config_entry.entry_id] = openuv except OpenUvError as err: _LOGGER.error('An error occurred: %s', str(err)) hass.components.persistent_notification.create( @@ -125,13 +166,9 @@ async def async_setup(hass, config): notification_id=NOTIFICATION_ID) return False - for component, schema in [ - ('binary_sensor', conf[CONF_BINARY_SENSORS]), - ('sensor', conf[CONF_SENSORS]), - ]: - hass.async_create_task( - discovery.async_load_platform( - hass, component, DOMAIN, schema, config)) + for component in ('binary_sensor', 'sensor'): + hass.async_create_task(hass.config_entries.async_forward_entry_setup( + config_entry, component)) async def refresh_sensors(event_time): """Refresh OpenUV data.""" @@ -139,7 +176,25 @@ async def async_setup(hass, config): await openuv.async_update() async_dispatcher_send(hass, TOPIC_UPDATE) - async_track_time_interval(hass, refresh_sensors, conf[CONF_SCAN_INTERVAL]) + hass.data[DOMAIN][DATA_OPENUV_LISTENER][ + config_entry.entry_id] = async_track_time_interval( + hass, refresh_sensors, + hass.data[DOMAIN][CONF_SCAN_INTERVAL]) + + return True + + +async def async_unload_entry(hass, config_entry): + """Unload an OpenUV config entry.""" + for component in ('binary_sensor', 'sensor'): + await hass.config_entries.async_forward_entry_unload( + config_entry, component) + + hass.data[DOMAIN][DATA_OPENUV_CLIENT].pop(config_entry.entry_id) + + remove_listener = hass.data[DOMAIN][DATA_OPENUV_LISTENER].pop( + config_entry.entry_id) + remove_listener() return True @@ -147,19 +202,20 @@ async def async_setup(hass, config): class OpenUV: """Define a generic OpenUV object.""" - def __init__(self, client, monitored_conditions): + def __init__(self, client, binary_sensor_conditions, sensor_conditions): """Initialize.""" - self._monitored_conditions = monitored_conditions + self.binary_sensor_conditions = binary_sensor_conditions self.client = client self.data = {} + self.sensor_conditions = sensor_conditions async def async_update(self): """Update sensor/binary sensor data.""" - if TYPE_PROTECTION_WINDOW in self._monitored_conditions: + if TYPE_PROTECTION_WINDOW in self.binary_sensor_conditions: data = await self.client.uv_protection_window() self.data[DATA_PROTECTION_WINDOW] = data - if any(c in self._monitored_conditions for c in SENSORS): + if any(c in self.sensor_conditions for c in SENSORS): data = await self.client.uv_index() self.data[DATA_UV] = data diff --git a/homeassistant/components/openuv/config_flow.py b/homeassistant/components/openuv/config_flow.py new file mode 100644 index 00000000000..55ee566268e --- /dev/null +++ b/homeassistant/components/openuv/config_flow.py @@ -0,0 +1,73 @@ +"""Config flow to configure the OpenUV component.""" + +from collections import OrderedDict + +import voluptuous as vol + +from homeassistant import config_entries, data_entry_flow +from homeassistant.core import callback +from homeassistant.const import ( + CONF_API_KEY, CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE) +from homeassistant.helpers import aiohttp_client, config_validation as cv + +from .const import DOMAIN + + +@callback +def configured_instances(hass): + """Return a set of configured OpenUV instances.""" + return set( + '{0}, {1}'.format( + entry.data[CONF_LATITUDE], entry.data[CONF_LONGITUDE]) + for entry in hass.config_entries.async_entries(DOMAIN)) + + +@config_entries.HANDLERS.register(DOMAIN) +class OpenUvFlowHandler(data_entry_flow.FlowHandler): + """Handle an OpenUV config flow.""" + + VERSION = 1 + + def __init__(self): + """Initialize the config flow.""" + pass + + async def async_step_import(self, import_config): + """Import a config entry from configuration.yaml.""" + return await self.async_step_user(import_config) + + async def async_step_user(self, user_input=None): + """Handle the start of the config flow.""" + from pyopenuv.util import validate_api_key + + errors = {} + + if user_input is not None: + identifier = '{0}, {1}'.format( + user_input.get(CONF_LATITUDE, self.hass.config.latitude), + user_input.get(CONF_LONGITUDE, self.hass.config.longitude)) + + if identifier in configured_instances(self.hass): + errors['base'] = 'identifier_exists' + else: + websession = aiohttp_client.async_get_clientsession(self.hass) + api_key_validation = await validate_api_key( + user_input[CONF_API_KEY], websession) + if api_key_validation: + return self.async_create_entry( + title=identifier, + data=user_input, + ) + errors['base'] = 'invalid_api_key' + + data_schema = OrderedDict() + data_schema[vol.Required(CONF_API_KEY)] = str + data_schema[vol.Optional(CONF_LATITUDE)] = cv.latitude + data_schema[vol.Optional(CONF_LONGITUDE)] = cv.longitude + data_schema[vol.Optional(CONF_ELEVATION)] = vol.Coerce(float) + + return self.async_show_form( + step_id='user', + data_schema=vol.Schema(data_schema), + errors=errors, + ) diff --git a/homeassistant/components/openuv/const.py b/homeassistant/components/openuv/const.py new file mode 100644 index 00000000000..1aa3d2abcaa --- /dev/null +++ b/homeassistant/components/openuv/const.py @@ -0,0 +1,3 @@ +"""Define constants for the OpenUV component.""" + +DOMAIN = 'openuv' diff --git a/homeassistant/components/openuv/strings.json b/homeassistant/components/openuv/strings.json new file mode 100644 index 00000000000..9c5af45619e --- /dev/null +++ b/homeassistant/components/openuv/strings.json @@ -0,0 +1,20 @@ +{ + "config": { + "title": "OpenUV", + "step": { + "user": { + "title": "Fill in your information", + "data": { + "api_key": "OpenUV API Key", + "elevation": "Elevation", + "latitude": "Latitude", + "longitude": "Longitude" + } + } + }, + "error": { + "identifier_exists": "Coordinates already registered", + "invalid_api_key": "Invalid API key" + } + } +} diff --git a/homeassistant/components/sensor/openuv.py b/homeassistant/components/sensor/openuv.py index aaa04590b3f..22712aa306b 100644 --- a/homeassistant/components/sensor/openuv.py +++ b/homeassistant/components/sensor/openuv.py @@ -6,13 +6,12 @@ https://home-assistant.io/components/sensor.openuv/ """ import logging -from homeassistant.const import CONF_MONITORED_CONDITIONS from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.components.openuv import ( - DATA_UV, DOMAIN, SENSORS, TOPIC_UPDATE, TYPE_CURRENT_OZONE_LEVEL, - TYPE_CURRENT_UV_INDEX, TYPE_CURRENT_UV_LEVEL, TYPE_MAX_UV_INDEX, - TYPE_SAFE_EXPOSURE_TIME_1, TYPE_SAFE_EXPOSURE_TIME_2, + DATA_OPENUV_CLIENT, DATA_UV, DOMAIN, SENSORS, TOPIC_UPDATE, + TYPE_CURRENT_OZONE_LEVEL, TYPE_CURRENT_UV_INDEX, TYPE_CURRENT_UV_LEVEL, + TYPE_MAX_UV_INDEX, TYPE_SAFE_EXPOSURE_TIME_1, TYPE_SAFE_EXPOSURE_TIME_2, TYPE_SAFE_EXPOSURE_TIME_3, TYPE_SAFE_EXPOSURE_TIME_4, TYPE_SAFE_EXPOSURE_TIME_5, TYPE_SAFE_EXPOSURE_TIME_6, OpenUvEntity) from homeassistant.util.dt import as_local, parse_datetime @@ -40,16 +39,20 @@ UV_LEVEL_LOW = "Low" async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): - """Set up the OpenUV binary sensor platform.""" - if discovery_info is None: - return + """Set up an OpenUV sensor based on existing config.""" + pass - openuv = hass.data[DOMAIN] + +async def async_setup_entry(hass, entry, async_add_entities): + """Set up a Nest sensor based on a config entry.""" + openuv = hass.data[DOMAIN][DATA_OPENUV_CLIENT][entry.entry_id] sensors = [] - for sensor_type in discovery_info[CONF_MONITORED_CONDITIONS]: + for sensor_type in openuv.sensor_conditions: name, icon, unit = SENSORS[sensor_type] - sensors.append(OpenUvSensor(openuv, sensor_type, name, icon, unit)) + sensors.append( + OpenUvSensor( + openuv, sensor_type, name, icon, unit, entry.entry_id)) async_add_entities(sensors, True) @@ -57,10 +60,12 @@ async def async_setup_platform( class OpenUvSensor(OpenUvEntity): """Define a binary sensor for OpenUV.""" - def __init__(self, openuv, sensor_type, name, icon, unit): + def __init__(self, openuv, sensor_type, name, icon, unit, entry_id): """Initialize the sensor.""" super().__init__(openuv) + self._dispatch_remove = None + self._entry_id = entry_id self._icon = icon self._latitude = openuv.client.latitude self._longitude = openuv.client.longitude @@ -102,7 +107,9 @@ class OpenUvSensor(OpenUvEntity): async def async_added_to_hass(self): """Register callbacks.""" - async_dispatcher_connect(self.hass, TOPIC_UPDATE, self._update_data) + self._dispatch_remove = async_dispatcher_connect( + self.hass, TOPIC_UPDATE, self._update_data) + self.async_on_remove(self._dispatch_remove) async def async_update(self): """Update the state.""" @@ -125,8 +132,7 @@ class OpenUvSensor(OpenUvEntity): elif self._sensor_type == TYPE_MAX_UV_INDEX: self._state = data['uv_max'] self._attrs.update({ - ATTR_MAX_UV_TIME: as_local( - parse_datetime(data['uv_max_time'])) + ATTR_MAX_UV_TIME: as_local(parse_datetime(data['uv_max_time'])) }) elif self._sensor_type in (TYPE_SAFE_EXPOSURE_TIME_1, TYPE_SAFE_EXPOSURE_TIME_2, diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 6eae9e13030..15932f2c3f8 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -141,6 +141,7 @@ FLOWS = [ 'homematicip_cloud', 'hue', 'nest', + 'openuv', 'sonos', 'zone', ] diff --git a/requirements_all.txt b/requirements_all.txt index d8d73cc36a1..b155612350b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -990,7 +990,7 @@ pynut2==2.1.2 pynx584==0.4 # homeassistant.components.openuv -pyopenuv==1.0.1 +pyopenuv==1.0.4 # homeassistant.components.iota pyota==2.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0ee02e0d109..b9e44445114 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -154,6 +154,9 @@ pymonoprice==0.3 # homeassistant.components.binary_sensor.nx584 pynx584==0.4 +# homeassistant.components.openuv +pyopenuv==1.0.4 + # homeassistant.auth.mfa_modules.totp # homeassistant.components.sensor.otp pyotp==2.2.6 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 4b694ec7ec0..fc8e67b1ab6 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -78,6 +78,7 @@ TEST_REQUIREMENTS = ( 'pylitejet', 'pymonoprice', 'pynx584', + 'pyopenuv', 'pyotp', 'pyqwikswitch', 'PyRMVtransport', diff --git a/tests/components/openuv/__init__.py b/tests/components/openuv/__init__.py new file mode 100644 index 00000000000..0e3595b1e51 --- /dev/null +++ b/tests/components/openuv/__init__.py @@ -0,0 +1 @@ +"""Define tests for the OpenUV component.""" diff --git a/tests/components/openuv/test_config_flow.py b/tests/components/openuv/test_config_flow.py new file mode 100644 index 00000000000..0e50bddabde --- /dev/null +++ b/tests/components/openuv/test_config_flow.py @@ -0,0 +1,93 @@ +"""Define tests for the OpenUV config flow.""" +from unittest.mock import patch + +from homeassistant import data_entry_flow +from homeassistant.components.openuv import DOMAIN, config_flow +from homeassistant.const import ( + CONF_API_KEY, CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE) + +from tests.common import MockConfigEntry, mock_coro + + +async def test_duplicate_error(hass): + """Test that errors are shown when duplicates are added.""" + conf = { + CONF_API_KEY: '12345abcde', + CONF_ELEVATION: 59.1234, + CONF_LATITUDE: 39.128712, + CONF_LONGITUDE: -104.9812612, + } + + MockConfigEntry(domain=DOMAIN, data=conf).add_to_hass(hass) + flow = config_flow.OpenUvFlowHandler() + flow.hass = hass + + result = await flow.async_step_user(user_input=conf) + assert result['errors'] == {'base': 'identifier_exists'} + + +@patch('pyopenuv.util.validate_api_key', return_value=mock_coro(False)) +async def test_invalid_api_key(hass): + """Test that an invalid API key throws an error.""" + conf = { + CONF_API_KEY: '12345abcde', + CONF_ELEVATION: 59.1234, + CONF_LATITUDE: 39.128712, + CONF_LONGITUDE: -104.9812612, + } + + flow = config_flow.OpenUvFlowHandler() + flow.hass = hass + + result = await flow.async_step_user(user_input=conf) + assert result['errors'] == {'base': 'invalid_api_key'} + + +async def test_show_form(hass): + """Test that the form is served with no input.""" + flow = config_flow.OpenUvFlowHandler() + flow.hass = hass + + result = await flow.async_step_user(user_input=None) + + assert result['type'] == data_entry_flow.RESULT_TYPE_FORM + assert result['step_id'] == 'user' + + +@patch('pyopenuv.util.validate_api_key', return_value=mock_coro(True)) +async def test_step_import(hass): + """Test that the import step works.""" + conf = { + CONF_API_KEY: '12345abcde', + } + + flow = config_flow.OpenUvFlowHandler() + flow.hass = hass + + result = await flow.async_step_import(import_config=conf) + + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result['title'] == '{0}, {1}'.format( + hass.config.latitude, hass.config.longitude) + assert result['data'] == conf + + +@patch('pyopenuv.util.validate_api_key', return_value=mock_coro(True)) +async def test_step_user(hass): + """Test that the user step works.""" + conf = { + CONF_API_KEY: '12345abcde', + CONF_ELEVATION: 59.1234, + CONF_LATITUDE: 39.128712, + CONF_LONGITUDE: -104.9812612, + } + + flow = config_flow.OpenUvFlowHandler() + flow.hass = hass + + result = await flow.async_step_user(user_input=conf) + + assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result['title'] == '{0}, {1}'.format( + conf[CONF_LATITUDE], conf[CONF_LONGITUDE]) + assert result['data'] == conf From 8fa999258915f84c0941986f578d81bec7cf5114 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Tue, 4 Sep 2018 09:24:42 +0200 Subject: [PATCH 041/357] Service to load new deCONZ devices without restart (#16308) * New service to load new devices from deCONZ without restarting HASS * Do not use len to check if list is empty * Add support for scenes to be updated as well * Rework refresh devices method * Fix test --- homeassistant/components/deconz/__init__.py | 54 +++++++++++++++++-- homeassistant/components/deconz/services.yaml | 4 +- homeassistant/components/scene/deconz.py | 19 ++++--- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/scene/test_deconz.py | 1 + 6 files changed, 70 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/deconz/__init__.py b/homeassistant/components/deconz/__init__.py index e9f797d95f9..6ed0a6e2c11 100644 --- a/homeassistant/components/deconz/__init__.py +++ b/homeassistant/components/deconz/__init__.py @@ -24,7 +24,7 @@ from .const import ( CONF_ALLOW_CLIP_SENSOR, CONFIG_FILE, DATA_DECONZ_EVENT, DATA_DECONZ_ID, DATA_DECONZ_UNSUB, DOMAIN, _LOGGER) -REQUIREMENTS = ['pydeconz==45'] +REQUIREMENTS = ['pydeconz==47'] CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ @@ -46,6 +46,8 @@ SERVICE_SCHEMA = vol.Schema({ vol.Required(SERVICE_DATA): dict, }) +SERVICE_DEVICE_REFRESH = 'device_refresh' + async def async_setup(hass, config): """Load configuration for deCONZ component. @@ -84,15 +86,17 @@ async def async_setup_entry(hass, config_entry): @callback def async_add_device_callback(device_type, device): """Handle event of new device creation in deCONZ.""" + if not isinstance(device, list): + device = [device] async_dispatcher_send( - hass, 'deconz_new_{}'.format(device_type), [device]) + hass, 'deconz_new_{}'.format(device_type), device) session = aiohttp_client.async_get_clientsession(hass) deconz = DeconzSession(hass.loop, session, **config_entry.data, async_add_device=async_add_device_callback) result = await deconz.async_load_parameters() + if result is False: - _LOGGER.error("Failed to communicate with deCONZ") return False hass.data[DOMAIN] = deconz @@ -149,16 +153,60 @@ async def async_setup_entry(hass, config_entry): data = call.data.get(SERVICE_DATA) deconz = hass.data[DOMAIN] if entity_id: + entities = hass.data.get(DATA_DECONZ_ID) + if entities: field = entities.get(entity_id) + if field is None: _LOGGER.error('Could not find the entity %s', entity_id) return + await deconz.async_put_state(field, data) + hass.services.async_register( DOMAIN, SERVICE_DECONZ, async_configure, schema=SERVICE_SCHEMA) + async def async_refresh_devices(call): + """Refresh available devices from deCONZ.""" + deconz = hass.data[DOMAIN] + + groups = list(deconz.groups.keys()) + lights = list(deconz.lights.keys()) + scenes = list(deconz.scenes.keys()) + sensors = list(deconz.sensors.keys()) + + if not await deconz.async_load_parameters(): + return + + async_add_device_callback( + 'group', [group + for group_id, group in deconz.groups.items() + if group_id not in groups] + ) + + async_add_device_callback( + 'light', [light + for light_id, light in deconz.lights.items() + if light_id not in lights] + ) + + async_add_device_callback( + 'scene', [scene + for scene_id, scene in deconz.scenes.items() + if scene_id not in scenes] + ) + + async_add_device_callback( + 'sensor', [sensor + for sensor_id, sensor in deconz.sensors.items() + if sensor_id not in sensors] + ) + + hass.services.async_register( + DOMAIN, SERVICE_DEVICE_REFRESH, async_refresh_devices) + @callback def deconz_shutdown(event): """ diff --git a/homeassistant/components/deconz/services.yaml b/homeassistant/components/deconz/services.yaml index 78bf7041a93..fa0fb8e14a4 100644 --- a/homeassistant/components/deconz/services.yaml +++ b/homeassistant/components/deconz/services.yaml @@ -1,4 +1,3 @@ - configure: description: Set attribute of device in deCONZ. See https://home-assistant.io/components/deconz/#device-services for details. fields: @@ -11,3 +10,6 @@ configure: data: description: Data is a json object with what data you want to alter. example: '{"on": true}' + +device_refresh: + description: Refresh device lists from deCONZ. \ No newline at end of file diff --git a/homeassistant/components/scene/deconz.py b/homeassistant/components/scene/deconz.py index 5af8f657206..b8fca6d8630 100644 --- a/homeassistant/components/scene/deconz.py +++ b/homeassistant/components/scene/deconz.py @@ -5,8 +5,10 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/scene.deconz/ """ from homeassistant.components.deconz import ( - DOMAIN as DATA_DECONZ, DATA_DECONZ_ID) + DOMAIN as DATA_DECONZ, DATA_DECONZ_ID, DATA_DECONZ_UNSUB) from homeassistant.components.scene import Scene +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect DEPENDENCIES = ['deconz'] @@ -19,12 +21,17 @@ async def async_setup_platform(hass, config, async_add_entities, async def async_setup_entry(hass, config_entry, async_add_entities): """Set up scenes for deCONZ component.""" - scenes = hass.data[DATA_DECONZ].scenes - entities = [] + @callback + def async_add_scene(scenes): + """Add scene from deCONZ.""" + entities = [] + for scene in scenes: + entities.append(DeconzScene(scene)) + async_add_entities(entities) + hass.data[DATA_DECONZ_UNSUB].append( + async_dispatcher_connect(hass, 'deconz_new_scene', async_add_scene)) - for scene in scenes.values(): - entities.append(DeconzScene(scene)) - async_add_entities(entities) + async_add_scene(hass.data[DATA_DECONZ].scenes.values()) class DeconzScene(Scene): diff --git a/requirements_all.txt b/requirements_all.txt index b155612350b..5f4fd64a6a2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -820,7 +820,7 @@ pycsspeechtts==1.0.2 pydaikin==0.4 # homeassistant.components.deconz -pydeconz==45 +pydeconz==47 # homeassistant.components.zwave pydispatcher==2.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b9e44445114..236033e2f19 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -139,7 +139,7 @@ py-canary==0.5.0 pyblackbird==0.5 # homeassistant.components.deconz -pydeconz==45 +pydeconz==47 # homeassistant.components.zwave pydispatcher==2.0.5 diff --git a/tests/components/scene/test_deconz.py b/tests/components/scene/test_deconz.py index 53f25808be2..8c22f718fa0 100644 --- a/tests/components/scene/test_deconz.py +++ b/tests/components/scene/test_deconz.py @@ -33,6 +33,7 @@ async def setup_bridge(hass, data): return_value=mock_coro(data)): await bridge.async_load_parameters() hass.data[deconz.DOMAIN] = bridge + hass.data[deconz.DATA_DECONZ_UNSUB] = [] hass.data[deconz.DATA_DECONZ_ID] = {} config_entry = config_entries.ConfigEntry( 1, deconz.DOMAIN, 'Mock Title', {'host': 'mock-host'}, 'test') From e61ac1a4a1ec03f9852ff48e585a602d494bc80e Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Tue, 4 Sep 2018 01:31:45 -0700 Subject: [PATCH 042/357] Delegate mqtt topic match validation to the paho mqtt client (#16403) * Delegate mqtt match topics to the paho mqtt client * Fixing linting error with importing MQTTMatcher --- homeassistant/components/mqtt/__init__.py | 26 +++++++---------------- tests/components/mqtt/test_init.py | 9 ++++++++ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 71be9c2435e..6bb08d7e8e5 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -13,7 +13,6 @@ import os import socket import time import ssl -import re import requests.certs import attr @@ -727,23 +726,14 @@ def _raise_on_error(result_code: int) -> None: def _match_topic(subscription: str, topic: str) -> bool: """Test if topic matches subscription.""" - reg_ex_parts = [] # type: List[str] - suffix = "" - if subscription.endswith('#'): - subscription = subscription[:-2] - suffix = "(.*)" - sub_parts = subscription.split('/') - for sub_part in sub_parts: - if sub_part == "+": - reg_ex_parts.append(r"([^\/]+)") - else: - reg_ex_parts.append(re.escape(sub_part)) - - reg_ex = "^" + (r'\/'.join(reg_ex_parts)) + suffix + "$" - - reg = re.compile(reg_ex) - - return reg.match(topic) is not None + from paho.mqtt.matcher import MQTTMatcher + matcher = MQTTMatcher() + matcher[subscription] = True + try: + next(matcher.iter_match(topic)) + return True + except StopIteration: + return False class MqttAvailability(Entity): diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index ecbc7cb9b02..51bd75f66e3 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -277,6 +277,15 @@ class TestMQTTCallbacks(unittest.TestCase): self.hass.block_till_done() self.assertEqual(0, len(self.calls)) + def test_subscribe_topic_level_wildcard_root_topic_no_subtree_match(self): + """Test the subscription of wildcard topics.""" + mqtt.subscribe(self.hass, 'test-topic/#', self.record_calls) + + fire_mqtt_message(self.hass, 'test-topic-123', 'test-payload') + + self.hass.block_till_done() + self.assertEqual(0, len(self.calls)) + def test_subscribe_topic_subtree_wildcard_subtree_topic(self): """Test the subscription of wildcard topics.""" mqtt.subscribe(self.hass, 'test-topic/#', self.record_calls) From 85658b6dd17a200b57b7ee5728a3009ce24b359d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=B8yer=20Iversen?= Date: Tue, 4 Sep 2018 10:50:12 +0200 Subject: [PATCH 043/357] Clean up dlink and some bug fix (#16346) * Update dlink.py * style * style --- homeassistant/components/switch/dlink.py | 80 +++++++++++++++--------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/switch/dlink.py b/homeassistant/components/switch/dlink.py index f4eaefcae20..91ef546ea22 100644 --- a/homeassistant/components/switch/dlink.py +++ b/homeassistant/components/switch/dlink.py @@ -5,14 +5,17 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/switch.dlink/ """ import logging +import urllib +from datetime import timedelta import voluptuous as vol -from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) -from homeassistant.const import ( - CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME) import homeassistant.helpers.config_validation as cv -from homeassistant.const import TEMP_CELSIUS, STATE_UNKNOWN +from homeassistant.util import dt as dt_util +from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) +from homeassistant.const import (ATTR_TEMPERATURE, + CONF_HOST, CONF_NAME, CONF_PASSWORD, + CONF_USERNAME, TEMP_CELSIUS) REQUIREMENTS = ['pyW215==0.6.0'] @@ -23,9 +26,7 @@ DEFAULT_PASSWORD = '' DEFAULT_USERNAME = 'admin' CONF_USE_LEGACY_PROTOCOL = 'use_legacy_protocol' -ATTR_CURRENT_CONSUMPTION = 'power_consumption' ATTR_TOTAL_CONSUMPTION = 'total_consumption' -ATTR_TEMPERATURE = 'temperature' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, @@ -35,6 +36,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, }) +SCAN_INTERVAL = timedelta(minutes=2) + def setup_platform(hass, config, add_entities, discovery_info=None): """Set up a D-Link Smart Plug.""" @@ -46,10 +49,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None): use_legacy_protocol = config.get(CONF_USE_LEGACY_PROTOCOL) name = config.get(CONF_NAME) - data = SmartPlugData(SmartPlug(host, - password, - username, - use_legacy_protocol)) + smartplug = SmartPlug(host, + password, + username, + use_legacy_protocol) + data = SmartPlugData(smartplug) add_entities([SmartPlugSwitch(hass, data, name)], True) @@ -74,37 +78,28 @@ class SmartPlugSwitch(SwitchDevice): try: ui_temp = self.units.temperature(int(self.data.temperature), TEMP_CELSIUS) - temperature = "%i %s" % \ - (ui_temp, self.units.temperature_unit) + temperature = ui_temp except (ValueError, TypeError): - temperature = STATE_UNKNOWN + temperature = None try: - current_consumption = "%.2f W" % \ - float(self.data.current_consumption) - except ValueError: - current_consumption = STATE_UNKNOWN - - try: - total_consumption = "%.1f kWh" % \ - float(self.data.total_consumption) - except ValueError: - total_consumption = STATE_UNKNOWN + total_consumption = float(self.data.total_consumption) + except (ValueError, TypeError): + total_consumption = None attrs = { - ATTR_CURRENT_CONSUMPTION: current_consumption, ATTR_TOTAL_CONSUMPTION: total_consumption, - ATTR_TEMPERATURE: temperature + ATTR_TEMPERATURE: temperature, } return attrs @property - def current_power_watt(self): + def current_power_w(self): """Return the current power usage in Watt.""" try: return float(self.data.current_consumption) - except ValueError: + except (ValueError, TypeError): return None @property @@ -124,6 +119,11 @@ class SmartPlugSwitch(SwitchDevice): """Get the latest data from the smart plug and updates the states.""" self.data.update() + @property + def available(self) -> bool: + """Return True if entity is available.""" + return self.data.available + class SmartPlugData: """Get the latest data from smart plug.""" @@ -135,10 +135,34 @@ class SmartPlugData: self.temperature = None self.current_consumption = None self.total_consumption = None + self.available = False + self._n_tried = 0 + self._last_tried = None def update(self): """Get the latest data from the smart plug.""" - self.state = self.smartplug.state + if self._last_tried is not None: + last_try_s = (dt_util.now() - self._last_tried).total_seconds()/60 + retry_seconds = min(self._n_tried*2, 10) - last_try_s + if self._n_tried > 0 and retry_seconds > 0: + _LOGGER.warning("Waiting %s s to retry", retry_seconds) + return + + _state = 'unknown' + try: + self._last_tried = dt_util.now() + _state = self.smartplug.state + except urllib.error.HTTPError: + _LOGGER.error("Dlink connection problem") + if _state == 'unknown': + self._n_tried += 1 + self.available = False + _LOGGER.warning("Failed to connect to dlink switch.") + return + self.state = _state + self.available = True + self.temperature = self.smartplug.temperature self.current_consumption = self.smartplug.current_consumption self.total_consumption = self.smartplug.total_consumption + self._n_tried = 0 From 3bd12fcef6afb25d7cbf76191f320571f7af01f7 Mon Sep 17 00:00:00 2001 From: Toon Willems Date: Tue, 4 Sep 2018 11:15:02 +0200 Subject: [PATCH 044/357] Implement correct state for RFlink cover (#16304) * implement correct state for rflink cover * Fix linting error * invert logic as local testing pointed out it should be reversed * add period at the end to satisfy the linter --- homeassistant/components/cover/rflink.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cover/rflink.py b/homeassistant/components/cover/rflink.py index e50fa488b92..41a4c2af045 100644 --- a/homeassistant/components/cover/rflink.py +++ b/homeassistant/components/cover/rflink.py @@ -92,9 +92,9 @@ class RflinkCover(RflinkCommand, CoverDevice): self.cancel_queued_send_commands() command = event['command'] - if command in ['on', 'allon']: + if command in ['on', 'allon', 'up']: self._state = True - elif command in ['off', 'alloff']: + elif command in ['off', 'alloff', 'down']: self._state = False @property @@ -105,7 +105,12 @@ class RflinkCover(RflinkCommand, CoverDevice): @property def is_closed(self): """Return if the cover is closed.""" - return None + return not self._state + + @property + def assumed_state(self): + """Return True because covers can be stopped midway.""" + return True def async_close_cover(self, **kwargs): """Turn the device close.""" From e1501c83f8d983becc557b7cb89ec746746495bb Mon Sep 17 00:00:00 2001 From: Paul Annekov Date: Tue, 4 Sep 2018 22:03:30 +0300 Subject: [PATCH 045/357] Fix Mi Flora median calculation (#16085) * fixed median was based on 1.5 minute interval, not 1 hour * ignore median and set state from first value, when previous state was None * update before add, removed unused 'retries' and 'ble_timeout', check if platform ready * added missing blank line * fixed too long line * using modern python 3.5 features, changed comment to be less verbose * continuation line fix * removed DEFAULT_SCAN_INTERVAL in favor of existing SCAN_INTERVAL --- homeassistant/components/sensor/miflora.py | 38 ++++++++++++---------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/sensor/miflora.py b/homeassistant/components/sensor/miflora.py index ced17512089..6f0fb3aba30 100644 --- a/homeassistant/components/sensor/miflora.py +++ b/homeassistant/components/sensor/miflora.py @@ -4,16 +4,19 @@ Support for Xiaomi Mi Flora BLE plant sensor. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.miflora/ """ +import asyncio +from datetime import timedelta import logging - import voluptuous as vol +import async_timeout from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv +from homeassistant.exceptions import PlatformNotReady from homeassistant.const import ( - CONF_FORCE_UPDATE, CONF_MONITORED_CONDITIONS, CONF_NAME, CONF_MAC -) + CONF_FORCE_UPDATE, CONF_MONITORED_CONDITIONS, CONF_NAME, CONF_MAC, + CONF_SCAN_INTERVAL) REQUIREMENTS = ['miflora==0.4.0'] @@ -21,19 +24,14 @@ REQUIREMENTS = ['miflora==0.4.0'] _LOGGER = logging.getLogger(__name__) CONF_ADAPTER = 'adapter' -CONF_CACHE = 'cache_value' CONF_MEDIAN = 'median' -CONF_RETRIES = 'retries' -CONF_TIMEOUT = 'timeout' DEFAULT_ADAPTER = 'hci0' -DEFAULT_UPDATE_INTERVAL = 1200 DEFAULT_FORCE_UPDATE = False DEFAULT_MEDIAN = 3 DEFAULT_NAME = 'Mi Flora' -DEFAULT_RETRIES = 2 -DEFAULT_TIMEOUT = 10 +SCAN_INTERVAL = timedelta(seconds=1200) # Sensor types are defined like: Name, units SENSOR_TYPES = { @@ -51,14 +49,12 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_MEDIAN, default=DEFAULT_MEDIAN): cv.positive_int, vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean, - vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, - vol.Optional(CONF_RETRIES, default=DEFAULT_RETRIES): cv.positive_int, - vol.Optional(CONF_CACHE, default=DEFAULT_UPDATE_INTERVAL): cv.positive_int, vol.Optional(CONF_ADAPTER, default=DEFAULT_ADAPTER): cv.string, }) -def setup_platform(hass, config, add_entities, discovery_info=None): +async def async_setup_platform(hass, config, async_add_entities, + discovery_info=None): """Set up the MiFlora sensor.""" from miflora import miflora_poller try: @@ -70,17 +66,22 @@ def setup_platform(hass, config, add_entities, discovery_info=None): backend = GatttoolBackend _LOGGER.debug('Miflora is using %s backend.', backend.__name__) - cache = config.get(CONF_CACHE) + cache = config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL).total_seconds() poller = miflora_poller.MiFloraPoller( config.get(CONF_MAC), cache_timeout=cache, adapter=config.get(CONF_ADAPTER), backend=backend) force_update = config.get(CONF_FORCE_UPDATE) median = config.get(CONF_MEDIAN) - poller.ble_timeout = config.get(CONF_TIMEOUT) - poller.retries = config.get(CONF_RETRIES) devs = [] + try: + with async_timeout.timeout(9): + await hass.async_add_executor_job(poller.fill_cache) + except asyncio.TimeoutError: + _LOGGER.error('Unable to connect to %s', config.get(CONF_MAC)) + raise PlatformNotReady + for parameter in config[CONF_MONITORED_CONDITIONS]: name = SENSOR_TYPES[parameter][0] unit = SENSOR_TYPES[parameter][1] @@ -92,7 +93,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): devs.append(MiFloraSensor( poller, parameter, name, unit, force_update, median)) - add_entities(devs) + async_add_entities(devs, update_before_add=True) class MiFloraSensor(Entity): @@ -171,5 +172,8 @@ class MiFloraSensor(Entity): median = sorted(self.data)[int((self.median_count - 1) / 2)] _LOGGER.debug("Median is: %s", median) self._state = median + elif self._state is None: + _LOGGER.debug("Set initial state") + self._state = self.data[0] else: _LOGGER.debug("Not yet enough data for median calculation") From 746f4ac1585ac47be3bcc5d06d79b54da4a4e900 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 4 Sep 2018 21:16:24 +0200 Subject: [PATCH 046/357] Add context to scripts and automations (#16415) * Add context to script helper * Update script component * Add context to automations * Lint --- .../components/automation/__init__.py | 91 ++++++++----------- homeassistant/components/automation/event.py | 4 +- .../components/automation/homeassistant.py | 8 +- .../components/automation/numeric_state.py | 4 +- homeassistant/components/automation/state.py | 4 +- .../components/automation/template.py | 4 +- homeassistant/components/automation/zone.py | 4 +- homeassistant/components/script.py | 57 +++++------- homeassistant/helpers/script.py | 46 ++++++---- homeassistant/helpers/service.py | 4 +- tests/components/automation/test_event.py | 7 +- .../automation/test_numeric_state.py | 10 +- tests/components/automation/test_state.py | 6 +- tests/components/automation/test_template.py | 12 +-- tests/components/automation/test_zone.py | 6 +- tests/components/test_script.py | 13 ++- tests/helpers/test_script.py | 28 ++++-- 17 files changed, 164 insertions(+), 144 deletions(-) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index c6c0af90d15..43fd4cedb88 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -158,27 +158,26 @@ def async_reload(hass): return hass.services.async_call(DOMAIN, SERVICE_RELOAD) -@asyncio.coroutine -def async_setup(hass, config): +async def async_setup(hass, config): """Set up the automation.""" component = EntityComponent(_LOGGER, DOMAIN, hass, group_name=GROUP_NAME_ALL_AUTOMATIONS) - yield from _async_process_config(hass, config, component) + await _async_process_config(hass, config, component) - @asyncio.coroutine - def trigger_service_handler(service_call): + async def trigger_service_handler(service_call): """Handle automation triggers.""" tasks = [] for entity in component.async_extract_from_service(service_call): tasks.append(entity.async_trigger( - service_call.data.get(ATTR_VARIABLES), True)) + service_call.data.get(ATTR_VARIABLES), + skip_condition=True, + context=service_call.context)) if tasks: - yield from asyncio.wait(tasks, loop=hass.loop) + await asyncio.wait(tasks, loop=hass.loop) - @asyncio.coroutine - def turn_onoff_service_handler(service_call): + async def turn_onoff_service_handler(service_call): """Handle automation turn on/off service calls.""" tasks = [] method = 'async_{}'.format(service_call.service) @@ -186,10 +185,9 @@ def async_setup(hass, config): tasks.append(getattr(entity, method)()) if tasks: - yield from asyncio.wait(tasks, loop=hass.loop) + await asyncio.wait(tasks, loop=hass.loop) - @asyncio.coroutine - def toggle_service_handler(service_call): + async def toggle_service_handler(service_call): """Handle automation toggle service calls.""" tasks = [] for entity in component.async_extract_from_service(service_call): @@ -199,15 +197,14 @@ def async_setup(hass, config): tasks.append(entity.async_turn_on()) if tasks: - yield from asyncio.wait(tasks, loop=hass.loop) + await asyncio.wait(tasks, loop=hass.loop) - @asyncio.coroutine - def reload_service_handler(service_call): + async def reload_service_handler(service_call): """Remove all automations and load new ones from config.""" - conf = yield from component.async_prepare_reload() + conf = await component.async_prepare_reload() if conf is None: return - yield from _async_process_config(hass, conf, component) + await _async_process_config(hass, conf, component) hass.services.async_register( DOMAIN, SERVICE_TRIGGER, trigger_service_handler, @@ -272,15 +269,14 @@ class AutomationEntity(ToggleEntity): """Return True if entity is on.""" return self._async_detach_triggers is not None - @asyncio.coroutine - def async_added_to_hass(self) -> None: + async def async_added_to_hass(self) -> None: """Startup with initial state or previous state.""" if self._initial_state is not None: enable_automation = self._initial_state _LOGGER.debug("Automation %s initial state %s from config " "initial_state", self.entity_id, enable_automation) else: - state = yield from async_get_last_state(self.hass, self.entity_id) + state = await async_get_last_state(self.hass, self.entity_id) if state: enable_automation = state.state == STATE_ON self._last_triggered = state.attributes.get('last_triggered') @@ -298,54 +294,50 @@ class AutomationEntity(ToggleEntity): # HomeAssistant is starting up if self.hass.state == CoreState.not_running: - @asyncio.coroutine - def async_enable_automation(event): + async def async_enable_automation(event): """Start automation on startup.""" - yield from self.async_enable() + await self.async_enable() self.hass.bus.async_listen_once( EVENT_HOMEASSISTANT_START, async_enable_automation) # HomeAssistant is running else: - yield from self.async_enable() + await self.async_enable() - @asyncio.coroutine - def async_turn_on(self, **kwargs) -> None: + async def async_turn_on(self, **kwargs) -> None: """Turn the entity on and update the state.""" if self.is_on: return - yield from self.async_enable() + await self.async_enable() - @asyncio.coroutine - def async_turn_off(self, **kwargs) -> None: + async def async_turn_off(self, **kwargs) -> None: """Turn the entity off.""" if not self.is_on: return self._async_detach_triggers() self._async_detach_triggers = None - yield from self.async_update_ha_state() + await self.async_update_ha_state() - @asyncio.coroutine - def async_trigger(self, variables, skip_condition=False): + async def async_trigger(self, variables, skip_condition=False, + context=None): """Trigger automation. This method is a coroutine. """ if skip_condition or self._cond_func(variables): - yield from self._async_action(self.entity_id, variables) + self.async_set_context(context) + await self._async_action(self.entity_id, variables, context) self._last_triggered = utcnow() - yield from self.async_update_ha_state() + await self.async_update_ha_state() - @asyncio.coroutine - def async_will_remove_from_hass(self): + async def async_will_remove_from_hass(self): """Remove listeners when removing automation from HASS.""" - yield from self.async_turn_off() + await self.async_turn_off() - @asyncio.coroutine - def async_enable(self): + async def async_enable(self): """Enable this automation entity. This method is a coroutine. @@ -353,9 +345,9 @@ class AutomationEntity(ToggleEntity): if self.is_on: return - self._async_detach_triggers = yield from self._async_attach_triggers( + self._async_detach_triggers = await self._async_attach_triggers( self.async_trigger) - yield from self.async_update_ha_state() + await self.async_update_ha_state() @property def device_state_attributes(self): @@ -368,8 +360,7 @@ class AutomationEntity(ToggleEntity): } -@asyncio.coroutine -def _async_process_config(hass, config, component): +async def _async_process_config(hass, config, component): """Process config and add automations. This method is a coroutine. @@ -411,20 +402,19 @@ def _async_process_config(hass, config, component): entities.append(entity) if entities: - yield from component.async_add_entities(entities) + await component.async_add_entities(entities) def _async_get_action(hass, config, name): """Return an action based on a configuration.""" script_obj = script.Script(hass, config, name) - @asyncio.coroutine - def action(entity_id, variables): + async def action(entity_id, variables, context): """Execute an action.""" _LOGGER.info('Executing %s', name) logbook.async_log_entry( hass, name, 'has been triggered', DOMAIN, entity_id) - yield from script_obj.async_run(variables) + await script_obj.async_run(variables, context) return action @@ -448,8 +438,7 @@ def _async_process_if(hass, config, p_config): return if_action -@asyncio.coroutine -def _async_process_trigger(hass, config, trigger_configs, name, action): +async def _async_process_trigger(hass, config, trigger_configs, name, action): """Set up the triggers. This method is a coroutine. @@ -457,13 +446,13 @@ def _async_process_trigger(hass, config, trigger_configs, name, action): removes = [] for conf in trigger_configs: - platform = yield from async_prepare_setup_platform( + platform = await async_prepare_setup_platform( hass, config, DOMAIN, conf.get(CONF_PLATFORM)) if platform is None: return None - remove = yield from platform.async_trigger(hass, conf, action) + remove = await platform.async_trigger(hass, conf, action) if not remove: _LOGGER.error("Error setting up trigger %s", name) diff --git a/homeassistant/components/automation/event.py b/homeassistant/components/automation/event.py index 7c035d7d1a5..e19a85edae6 100644 --- a/homeassistant/components/automation/event.py +++ b/homeassistant/components/automation/event.py @@ -45,11 +45,11 @@ def async_trigger(hass, config, action): # If event data doesn't match requested schema, skip event return - hass.async_run_job(action, { + hass.async_run_job(action({ 'trigger': { 'platform': 'event', 'event': event, }, - }) + }, context=event.context)) return hass.bus.async_listen(event_type, handle_event) diff --git a/homeassistant/components/automation/homeassistant.py b/homeassistant/components/automation/homeassistant.py index 74cf195bc61..b55d99f706a 100644 --- a/homeassistant/components/automation/homeassistant.py +++ b/homeassistant/components/automation/homeassistant.py @@ -32,12 +32,12 @@ def async_trigger(hass, config, action): @callback def hass_shutdown(event): """Execute when Home Assistant is shutting down.""" - hass.async_run_job(action, { + hass.async_run_job(action({ 'trigger': { 'platform': 'homeassistant', 'event': event, }, - }) + }, context=event.context)) return hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, hass_shutdown) @@ -45,11 +45,11 @@ def async_trigger(hass, config, action): # Automation are enabled while hass is starting up, fire right away # Check state because a config reload shouldn't trigger it. if hass.state == CoreState.starting: - hass.async_run_job(action, { + hass.async_run_job(action({ 'trigger': { 'platform': 'homeassistant', 'event': event, }, - }) + })) return lambda: None diff --git a/homeassistant/components/automation/numeric_state.py b/homeassistant/components/automation/numeric_state.py index b59271f25e5..f0dcbf0be57 100644 --- a/homeassistant/components/automation/numeric_state.py +++ b/homeassistant/components/automation/numeric_state.py @@ -66,7 +66,7 @@ def async_trigger(hass, config, action): @callback def call_action(): """Call action with right context.""" - hass.async_run_job(action, { + hass.async_run_job(action({ 'trigger': { 'platform': 'numeric_state', 'entity_id': entity, @@ -75,7 +75,7 @@ def async_trigger(hass, config, action): 'from_state': from_s, 'to_state': to_s, } - }) + }, context=to_s.context)) matching = check_numeric_state(entity, from_s, to_s) diff --git a/homeassistant/components/automation/state.py b/homeassistant/components/automation/state.py index 9243f960850..263d4158e25 100644 --- a/homeassistant/components/automation/state.py +++ b/homeassistant/components/automation/state.py @@ -43,7 +43,7 @@ def async_trigger(hass, config, action): @callback def call_action(): """Call action with right context.""" - hass.async_run_job(action, { + hass.async_run_job(action({ 'trigger': { 'platform': 'state', 'entity_id': entity, @@ -51,7 +51,7 @@ def async_trigger(hass, config, action): 'to_state': to_s, 'for': time_delta, } - }) + }, context=to_s.context)) # Ignore changes to state attributes if from/to is in use if (not match_all and from_s is not None and to_s is not None and diff --git a/homeassistant/components/automation/template.py b/homeassistant/components/automation/template.py index 0fcdeaae5e0..67a44f1a347 100644 --- a/homeassistant/components/automation/template.py +++ b/homeassistant/components/automation/template.py @@ -32,13 +32,13 @@ def async_trigger(hass, config, action): @callback def template_listener(entity_id, from_s, to_s): """Listen for state changes and calls action.""" - hass.async_run_job(action, { + hass.async_run_job(action({ 'trigger': { 'platform': 'template', 'entity_id': entity_id, 'from_state': from_s, 'to_state': to_s, }, - }) + }, context=to_s.context)) return async_track_template(hass, value_template, template_listener) diff --git a/homeassistant/components/automation/zone.py b/homeassistant/components/automation/zone.py index 61d846582cb..f30dfe753cb 100644 --- a/homeassistant/components/automation/zone.py +++ b/homeassistant/components/automation/zone.py @@ -51,7 +51,7 @@ def async_trigger(hass, config, action): # pylint: disable=too-many-boolean-expressions if event == EVENT_ENTER and not from_match and to_match or \ event == EVENT_LEAVE and from_match and not to_match: - hass.async_run_job(action, { + hass.async_run_job(action({ 'trigger': { 'platform': 'zone', 'entity_id': entity, @@ -60,7 +60,7 @@ def async_trigger(hass, config, action): 'zone': zone_state, 'event': event, }, - }) + }, context=to_s.context)) return async_track_state_change(hass, entity_id, zone_automation_listener, MATCH_ALL, MATCH_ALL) diff --git a/homeassistant/components/script.py b/homeassistant/components/script.py index a45f8ba8930..247ac07283e 100644 --- a/homeassistant/components/script.py +++ b/homeassistant/components/script.py @@ -63,11 +63,11 @@ def is_on(hass, entity_id): @bind_hass -def turn_on(hass, entity_id, variables=None): +def turn_on(hass, entity_id, variables=None, context=None): """Turn script on.""" _, object_id = split_entity_id(entity_id) - hass.services.call(DOMAIN, object_id, variables) + hass.services.call(DOMAIN, object_id, variables, context=context) @bind_hass @@ -97,45 +97,41 @@ def async_reload(hass): return hass.services.async_call(DOMAIN, SERVICE_RELOAD) -@asyncio.coroutine -def async_setup(hass, config): +async def async_setup(hass, config): """Load the scripts from the configuration.""" component = EntityComponent( _LOGGER, DOMAIN, hass, group_name=GROUP_NAME_ALL_SCRIPTS) - yield from _async_process_config(hass, config, component) + await _async_process_config(hass, config, component) - @asyncio.coroutine - def reload_service(service): + async def reload_service(service): """Call a service to reload scripts.""" - conf = yield from component.async_prepare_reload() + conf = await component.async_prepare_reload() if conf is None: return - yield from _async_process_config(hass, conf, component) + await _async_process_config(hass, conf, component) - @asyncio.coroutine - def turn_on_service(service): + async def turn_on_service(service): """Call a service to turn script on.""" # We could turn on script directly here, but we only want to offer # one way to do it. Otherwise no easy way to detect invocations. var = service.data.get(ATTR_VARIABLES) for script in component.async_extract_from_service(service): - yield from hass.services.async_call(DOMAIN, script.object_id, var) + await hass.services.async_call(DOMAIN, script.object_id, var, + context=service.context) - @asyncio.coroutine - def turn_off_service(service): + async def turn_off_service(service): """Cancel a script.""" # Stopping a script is ok to be done in parallel - yield from asyncio.wait( + await asyncio.wait( [script.async_turn_off() for script in component.async_extract_from_service(service)], loop=hass.loop) - @asyncio.coroutine - def toggle_service(service): + async def toggle_service(service): """Toggle a script.""" for script in component.async_extract_from_service(service): - yield from script.async_toggle() + await script.async_toggle(context=service.context) hass.services.async_register(DOMAIN, SERVICE_RELOAD, reload_service, schema=RELOAD_SERVICE_SCHEMA) @@ -149,18 +145,17 @@ def async_setup(hass, config): return True -@asyncio.coroutine -def _async_process_config(hass, config, component): - """Process group configuration.""" - @asyncio.coroutine - def service_handler(service): +async def _async_process_config(hass, config, component): + """Process script configuration.""" + async def service_handler(service): """Execute a service call to script.