forked from home-assistant/core
Compare commits
198 Commits
2021.8.0b0
...
2021.8.7
| Author | SHA1 | Date | |
|---|---|---|---|
| e0873493e2 | |||
| 700f149ef8 | |||
| 848885c658 | |||
| 5af94c42db | |||
| dd1ef7fa55 | |||
| 8d1bd55b68 | |||
| 3c3a6e6cb4 | |||
| 2f2038c147 | |||
| 5a5bbef1b8 | |||
| 0f60a5a8c0 | |||
| f43151081b | |||
| ab82369320 | |||
| 1c503244c6 | |||
| 482e00a071 | |||
| 6f3879fc92 | |||
| 6382061b57 | |||
| 1d45a80a92 | |||
| 4d01dd3c0c | |||
| 72e548de5f | |||
| 7d67caba44 | |||
| 5fc5b53c01 | |||
| 745afc8fe2 | |||
| c6fc5e35fe | |||
| b2b0cc9a22 | |||
| ebab5c5d92 | |||
| 56d51404b7 | |||
| bb3769c84f | |||
| 3effe94a27 | |||
| a21e3aed77 | |||
| 747eb92a4a | |||
| 5ac5b41a11 | |||
| 0b532c139c | |||
| d4290d1e03 | |||
| b4d466f87c | |||
| 4f3cf5a61c | |||
| cfa6040d55 | |||
| 3fcbde3b9c | |||
| 84f6586058 | |||
| cb14acd606 | |||
| 59c882a0f5 | |||
| b21f319b0a | |||
| 2a1d2b77a1 | |||
| 9caad5b2c7 | |||
| 5402173e98 | |||
| 985dab6bdf | |||
| cf92d45f07 | |||
| 955a72080f | |||
| 3edd505468 | |||
| 746bb2997e | |||
| 1809b7a98b | |||
| 903e2243e7 | |||
| f3de8b9f28 | |||
| fe9808926e | |||
| f458f330a5 | |||
| fb6aca4f8b | |||
| 724f11bb0d | |||
| 13ded1e5b2 | |||
| 5dcf5edae0 | |||
| 56d0ef34fd | |||
| 94e26df6d3 | |||
| 3a17e22982 | |||
| 1934159fd0 | |||
| 4fbe713079 | |||
| 9155274c6e | |||
| cb775029e0 | |||
| 011cda5fc4 | |||
| d580036dfb | |||
| 15e9310a01 | |||
| 93a2e2849c | |||
| 07f8236e6f | |||
| a07048aacf | |||
| 890d190612 | |||
| b00173705e | |||
| 374ccaae47 | |||
| fa4ec926ce | |||
| e37bf733bd | |||
| 52f7f203d2 | |||
| 557e1862d5 | |||
| fbfb77b900 | |||
| bc548050f1 | |||
| 5c9d7edadd | |||
| 6544a32319 | |||
| 5e9081e323 | |||
| 436d0e0fb4 | |||
| 72032edaec | |||
| 14621fc445 | |||
| ef55a8d2e2 | |||
| 82b2ae8e91 | |||
| 01299ddd20 | |||
| fcc82d26a4 | |||
| 16b5544104 | |||
| f0f4c13cbe | |||
| 4de3f031cc | |||
| 9ec516e1d4 | |||
| 38df475936 | |||
| af81dda1e2 | |||
| 4fdd354745 | |||
| 0342c0da33 | |||
| 7a8676dc83 | |||
| 54ac889362 | |||
| 4e2c174741 | |||
| e4fd43ed7c | |||
| 07604e60e5 | |||
| 922c0dc8be | |||
| 30497eff0e | |||
| 5536da18a6 | |||
| 120122ffe2 | |||
| 8b7bdc9b67 | |||
| d74ca25291 | |||
| be6cb2e792 | |||
| f7e448c8b2 | |||
| 2e1f42937d | |||
| 92cc51370d | |||
| a97e480d82 | |||
| 66711219c7 | |||
| b0f6e8c40a | |||
| df20d69fd2 | |||
| 249fb51d2f | |||
| e35b5dd7c1 | |||
| bebd495e74 | |||
| d56636ed37 | |||
| e9b672c0b4 | |||
| 5f8f1ae695 | |||
| 2e441d8b7c | |||
| 31af17f7f7 | |||
| 91af3b0502 | |||
| 1a1efecdba | |||
| 1c30967f6f | |||
| ab4ed128cc | |||
| 3d6ba793f7 | |||
| 9cee9d9d8a | |||
| e093e0bf10 | |||
| 1019bb059d | |||
| ec35b92052 | |||
| cb2103d96c | |||
| 3f7ddb4706 | |||
| fd0ae7ab36 | |||
| fc5c30775d | |||
| 0948eafb93 | |||
| 31869cbb12 | |||
| 2297c0b58b | |||
| e0fc14f82c | |||
| cd3390e012 | |||
| af96c5d60c | |||
| 9f0f40dac6 | |||
| 958df580a9 | |||
| 0aee659ee9 | |||
| 423fb910b5 | |||
| fc8af9af8e | |||
| a75c7d52c9 | |||
| dac968bf32 | |||
| 447901c223 | |||
| 9dcd3f6626 | |||
| d34bd8ad1e | |||
| 83e4e4f769 | |||
| 128dc07fa5 | |||
| d2dfdd81ad | |||
| 0442827b9e | |||
| 37c3062874 | |||
| bfacff5d78 | |||
| d54621e778 | |||
| 716c3f69ca | |||
| 630a1fb36c | |||
| 54eeebfd20 | |||
| a671a0ccac | |||
| 8cf0182f2f | |||
| f1400b03bb | |||
| 6dc00d3d87 | |||
| bf6133534d | |||
| b1758e1fcc | |||
| cc0aa32f3e | |||
| dc2494f0a0 | |||
| 4b2a1ec694 | |||
| b2187022c4 | |||
| c6f588fc08 | |||
| 462e3a3d0d | |||
| aa179a1ad9 | |||
| 1117158bd0 | |||
| 6ced0153df | |||
| 7f314e17de | |||
| 9ad29ae75c | |||
| 43a89dc452 | |||
| 268f0dd62f | |||
| d7768f13c1 | |||
| db8aa4658a | |||
| d19d487b21 | |||
| 2aeecba64c | |||
| b3367d8b3f | |||
| 7e6856ace8 | |||
| b5f0c2cef4 | |||
| 2ffc779f3d | |||
| 75dc55418b | |||
| 384ddbafab | |||
| 5483300668 | |||
| c9d355a8a4 | |||
| fce7417ed1 | |||
| f13d7f189a | |||
| 3265c7b8d8 |
+1
-6
@@ -75,12 +75,6 @@ omit =
|
||||
homeassistant/components/asuswrt/router.py
|
||||
homeassistant/components/aten_pe/*
|
||||
homeassistant/components/atome/*
|
||||
homeassistant/components/automate/__init__.py
|
||||
homeassistant/components/automate/base.py
|
||||
homeassistant/components/automate/const.py
|
||||
homeassistant/components/automate/cover.py
|
||||
homeassistant/components/automate/helpers.py
|
||||
homeassistant/components/automate/hub.py
|
||||
homeassistant/components/aurora/__init__.py
|
||||
homeassistant/components/aurora/binary_sensor.py
|
||||
homeassistant/components/aurora/const.py
|
||||
@@ -678,6 +672,7 @@ omit =
|
||||
homeassistant/components/mystrom/light.py
|
||||
homeassistant/components/mystrom/switch.py
|
||||
homeassistant/components/myq/__init__.py
|
||||
homeassistant/components/myq/cover.py
|
||||
homeassistant/components/nad/media_player.py
|
||||
homeassistant/components/nanoleaf/light.py
|
||||
homeassistant/components/neato/__init__.py
|
||||
|
||||
@@ -56,7 +56,6 @@ homeassistant/components/august/* @bdraco
|
||||
homeassistant/components/aurora/* @djtimca
|
||||
homeassistant/components/aurora_abb_powerone/* @davet2001
|
||||
homeassistant/components/auth/* @home-assistant/core
|
||||
homeassistant/components/automate/* @sillyfrog
|
||||
homeassistant/components/automation/* @home-assistant/core
|
||||
homeassistant/components/avea/* @pattyland
|
||||
homeassistant/components/awair/* @ahayworth @danielsjf
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"reauth_successful": "La reautenticaci\u00f3n fue exitosa",
|
||||
"single_instance_allowed": "Solo se permite una \u00fanica configuraci\u00f3n de Abode."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "No se pudo conectar",
|
||||
"invalid_auth": "Autenticaci\u00f3n inv\u00e1lida",
|
||||
"invalid_mfa_code": "C\u00f3digo MFA no v\u00e1lido"
|
||||
},
|
||||
"step": {
|
||||
@@ -15,7 +18,8 @@
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"password": "Contrase\u00f1a"
|
||||
"password": "Contrase\u00f1a",
|
||||
"username": "Correo electr\u00f3nico"
|
||||
},
|
||||
"title": "Complete su informaci\u00f3n de inicio de sesi\u00f3n de Abode"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"single_instance_allowed": "Ya configurado. Solo es posible una \u00fanica configuraci\u00f3n."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "No se pudo conectar",
|
||||
"invalid_api_key": "Clave de API no v\u00e1lida",
|
||||
"requests_exceeded": "Se super\u00f3 el n\u00famero permitido de solicitudes a la API de Accuweather. Tiene que esperar o cambiar la clave de API."
|
||||
},
|
||||
"step": {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/adax",
|
||||
"requirements": [
|
||||
"adax==0.0.1"
|
||||
"adax==0.1.1"
|
||||
],
|
||||
"codeowners": [
|
||||
"@danielhiversen"
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit",
|
||||
"invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Hostitel",
|
||||
"password": "Heslo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,17 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_auth": "Authentification invalide"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"account_id": "identifiant de compte",
|
||||
"host": "H\u00f4te",
|
||||
"password": "Mot de passe"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Impossibile connettersi",
|
||||
"invalid_auth": "Autenticazione non valida"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"account_id": "ID account",
|
||||
"host": "Host",
|
||||
"password": "Password"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,13 +91,13 @@ class AdsCover(AdsEntity, CoverEntity):
|
||||
):
|
||||
"""Initialize AdsCover entity."""
|
||||
super().__init__(ads_hub, name, ads_var_is_closed)
|
||||
if self._ads_var is None:
|
||||
if self._attr_unique_id is None:
|
||||
if ads_var_position is not None:
|
||||
self._unique_id = ads_var_position
|
||||
self._attr_unique_id = ads_var_position
|
||||
elif ads_var_pos_set is not None:
|
||||
self._unique_id = ads_var_pos_set
|
||||
self._attr_unique_id = ads_var_pos_set
|
||||
elif ads_var_open is not None:
|
||||
self._unique_id = ads_var_open
|
||||
self._attr_unique_id = ads_var_open
|
||||
|
||||
self._state_dict[STATE_KEY_POSITION] = None
|
||||
self._ads_var_position = ads_var_position
|
||||
|
||||
@@ -15,7 +15,6 @@ from homeassistant.components.climate.const import (
|
||||
SUPPORT_TARGET_TEMPERATURE,
|
||||
)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import entity_platform
|
||||
|
||||
from .const import (
|
||||
@@ -166,19 +165,22 @@ class AdvantageAirZone(AdvantageAirClimateEntity):
|
||||
f'{self.coordinator.data["system"]["rid"]}-{ac_key}-{zone_key}'
|
||||
)
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""When entity is added to hass."""
|
||||
self.async_on_remove(self.coordinator.async_add_listener(self._update_callback))
|
||||
|
||||
@callback
|
||||
def _update_callback(self) -> None:
|
||||
"""Load data from integration."""
|
||||
self._attr_current_temperature = self._zone["measuredTemp"]
|
||||
self._attr_target_temperature = self._zone["setTemp"]
|
||||
self._attr_hvac_mode = HVAC_MODE_OFF
|
||||
@property
|
||||
def hvac_mode(self):
|
||||
"""Return the current state as HVAC mode."""
|
||||
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
|
||||
self._attr_hvac_mode = HVAC_MODE_FAN_ONLY
|
||||
self.async_write_ha_state()
|
||||
return HVAC_MODE_FAN_ONLY
|
||||
return HVAC_MODE_OFF
|
||||
|
||||
@property
|
||||
def current_temperature(self):
|
||||
"""Return the current temperature."""
|
||||
return self._zone["measuredTemp"]
|
||||
|
||||
@property
|
||||
def target_temperature(self):
|
||||
"""Return the target temperature."""
|
||||
return self._zone["setTemp"]
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode):
|
||||
"""Set the HVAC Mode and State."""
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"name": "Name der Integration"
|
||||
},
|
||||
"description": "Richte die AEMET OpenData Integration ein. Um den API-Schl\u00fcssel zu generieren, besuche https://opendata.aemet.es/centrodedescargas/altaUsuario",
|
||||
"title": "[void]"
|
||||
"title": "AEMET OpenData"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -18,5 +18,14 @@
|
||||
"title": "AEMET OpenData"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"station_updates": "Recueillir les donn\u00e9es des stations m\u00e9t\u00e9orologiques AEMET"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,8 +67,6 @@ async def async_setup_entry(
|
||||
class AgentCamera(MjpegCamera):
|
||||
"""Representation of an Agent Device Stream."""
|
||||
|
||||
_attr_supported_features = SUPPORT_ON_OFF
|
||||
|
||||
def __init__(self, device):
|
||||
"""Initialize as a subclass of MjpegCamera."""
|
||||
device_info = {
|
||||
@@ -80,7 +78,6 @@ class AgentCamera(MjpegCamera):
|
||||
self._removed = False
|
||||
self._attr_name = f"{device.client.name} {device.name}"
|
||||
self._attr_unique_id = f"{device._client.unique}_{device.typeID}_{device.id}"
|
||||
self._attr_should_poll = True
|
||||
super().__init__(device_info)
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(AGENT_DOMAIN, self.unique_id)},
|
||||
@@ -102,10 +99,10 @@ class AgentCamera(MjpegCamera):
|
||||
if self.device.client.is_available and not self._removed:
|
||||
_LOGGER.error("%s lost", self.name)
|
||||
self._removed = True
|
||||
self._attr_available = self.device.client.is_available
|
||||
self._attr_icon = "mdi:camcorder-off"
|
||||
if self.is_on:
|
||||
self._attr_icon = "mdi:camcorder"
|
||||
self._attr_available = self.device.client.is_available
|
||||
self._attr_extra_state_attributes = {
|
||||
ATTR_ATTRIBUTION: ATTRIBUTION,
|
||||
"editable": False,
|
||||
@@ -117,6 +114,11 @@ class AgentCamera(MjpegCamera):
|
||||
"alerts_enabled": self.device.alerts_active,
|
||||
}
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""Update the state periodically."""
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_recording(self) -> bool:
|
||||
"""Return whether the monitor is recording."""
|
||||
@@ -137,6 +139,11 @@ class AgentCamera(MjpegCamera):
|
||||
"""Return True if entity is connected."""
|
||||
return self.device.connected
|
||||
|
||||
@property
|
||||
def supported_features(self) -> int:
|
||||
"""Return supported features."""
|
||||
return SUPPORT_ON_OFF
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if on."""
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
{
|
||||
"state": {
|
||||
"airvisual__pollutant_label": {
|
||||
"co": "Monoxyde de carbone"
|
||||
"co": "Monoxyde de carbone",
|
||||
"n2": "Dioxyde d'azote",
|
||||
"o3": "Ozone",
|
||||
"p1": "PM10",
|
||||
"p2": "PM2.5",
|
||||
"s2": "Dioxyde de soufre"
|
||||
},
|
||||
"airvisual__pollutant_level": {
|
||||
"good": "Bon",
|
||||
"hazardous": "Hasardeux"
|
||||
"hazardous": "Hasardeux",
|
||||
"moderate": "Mod\u00e9rer",
|
||||
"unhealthy": "Malsain",
|
||||
"unhealthy_sensitive": "Malsain pour les groupes sensibles",
|
||||
"very_unhealthy": "Tr\u00e8s malsain"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"state": {
|
||||
"airvisual__pollutant_level": {
|
||||
"good": "\u05d8\u05d5\u05d1",
|
||||
"unhealthy": "\u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"state": {
|
||||
"airvisual__pollutant_label": {
|
||||
"co": "Monossido di carbonio",
|
||||
"n2": "Anidride nitrosa",
|
||||
"o3": "Ozono",
|
||||
"p1": "PM10",
|
||||
"p2": "PM2.5",
|
||||
"s2": "Anidride solforosa"
|
||||
},
|
||||
"airvisual__pollutant_level": {
|
||||
"good": "Buono",
|
||||
"hazardous": "Pericoloso",
|
||||
"moderate": "Moderato",
|
||||
"unhealthy": "Malsano",
|
||||
"unhealthy_sensitive": "Malsano per gruppi sensibili",
|
||||
"very_unhealthy": "Molto malsano"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"state": {
|
||||
"airvisual__pollutant_label": {
|
||||
"p1": "PM10",
|
||||
"p2": "PM2.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,10 @@ def setup_platform(
|
||||
try:
|
||||
if not acc.login():
|
||||
raise ValueError("Username or Password is incorrect")
|
||||
add_entities(AladdinDevice(acc, door) for door in acc.get_doors())
|
||||
add_entities(
|
||||
(AladdinDevice(acc, door) for door in acc.get_doors()),
|
||||
update_before_add=True,
|
||||
)
|
||||
except (TypeError, KeyError, NameError, ValueError) as ex:
|
||||
_LOGGER.error("%s", ex)
|
||||
hass.components.persistent_notification.create(
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"arm_away": "Armer {entity_name} en mode \"sortie\"",
|
||||
"arm_home": "Armer {entity_name} en mode \"maison\"",
|
||||
"arm_night": "Armer {entity_name} en mode \"nuit\"",
|
||||
"arm_vacation": "Armer {entity_name} vacances",
|
||||
"disarm": "D\u00e9sarmer {entity_name}",
|
||||
"trigger": "D\u00e9clencheur {entity_name}"
|
||||
},
|
||||
@@ -29,6 +30,7 @@
|
||||
"armed_custom_bypass": "Arm\u00e9 avec exception personnalis\u00e9e",
|
||||
"armed_home": "Enclench\u00e9e (pr\u00e9sent)",
|
||||
"armed_night": "Enclench\u00e9 (nuit)",
|
||||
"armed_vacation": "Arm\u00e9es vacances",
|
||||
"arming": "Activation",
|
||||
"disarmed": "D\u00e9sactiv\u00e9e",
|
||||
"disarming": "D\u00e9sactivation",
|
||||
|
||||
@@ -111,13 +111,13 @@ class AlarmDecoderBinarySensor(BinarySensorEntity):
|
||||
def _fault_callback(self, zone):
|
||||
"""Update the zone's state, if needed."""
|
||||
if zone is None or int(zone) == self._zone_number:
|
||||
self._attr_state = 1
|
||||
self._attr_is_on = True
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def _restore_callback(self, zone):
|
||||
"""Update the zone's state, if needed."""
|
||||
if zone is None or (int(zone) == self._zone_number and not self._loop):
|
||||
self._attr_state = 0
|
||||
self._attr_is_on = False
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def _rfx_message_callback(self, message):
|
||||
@@ -125,7 +125,7 @@ class AlarmDecoderBinarySensor(BinarySensorEntity):
|
||||
if self._rfid and message and message.serial_number == self._rfid:
|
||||
rfstate = message.value
|
||||
if self._loop:
|
||||
self._attr_state = 1 if message.loop[self._loop - 1] else 0
|
||||
self._attr_is_on = bool(message.loop[self._loop - 1])
|
||||
attr = {CONF_ZONE_NUMBER: self._zone_number}
|
||||
if self._rfid and rfstate is not None:
|
||||
attr[ATTR_RF_BIT0] = bool(rfstate & 0x01)
|
||||
@@ -150,5 +150,5 @@ class AlarmDecoderBinarySensor(BinarySensorEntity):
|
||||
message.channel,
|
||||
message.value,
|
||||
)
|
||||
self._attr_state = message.value
|
||||
self._attr_is_on = bool(message.value)
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"reauth_successful": "R\u00e9-authentification r\u00e9ussie"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_api_key": "Cl\u00e9 API non valide"
|
||||
},
|
||||
"step": {
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_key": "cl\u00e9 API",
|
||||
"description": "R\u00e9-authentifiez-vous avec votre compte Ambee."
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "cl\u00e9 API",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"name": "Nom"
|
||||
},
|
||||
"description": "Configurer Ambee pour l'int\u00e9grer \u00e0 Home Assistant."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"state": {
|
||||
"ambee__risk": {
|
||||
"high": "Haute",
|
||||
"low": "Faible",
|
||||
"moderate": "Mod\u00e9rer",
|
||||
"very high": "Tr\u00e8s \u00e9lev\u00e9"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"state": {
|
||||
"ambee__risk": {
|
||||
"high": "\u05d2\u05d1\u05d5\u05d4",
|
||||
"low": "\u05e0\u05de\u05d5\u05da",
|
||||
"very high": "\u05d2\u05d1\u05d5\u05d4 \u05de\u05d0\u05d5\u05d3"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,8 +154,6 @@ class AmbiclimateEntity(ClimateEntity):
|
||||
"name": self.name,
|
||||
"manufacturer": "Ambiclimate",
|
||||
}
|
||||
self._attr_min_temp = heater.get_min_temp()
|
||||
self._attr_max_temp = heater.get_max_temp()
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature."""
|
||||
@@ -184,6 +182,8 @@ class AmbiclimateEntity(ClimateEntity):
|
||||
await self._store.async_save(token_info)
|
||||
|
||||
data = await self._heater.update_device()
|
||||
self._attr_min_temp = self._heater.get_min_temp()
|
||||
self._attr_max_temp = self._heater.get_max_temp()
|
||||
self._attr_target_temperature = data.get("target_temperature")
|
||||
self._attr_current_temperature = data.get("temperature")
|
||||
self._attr_current_humidity = data.get("humidity")
|
||||
|
||||
@@ -8,6 +8,10 @@ import async_timeout
|
||||
from homeassistant.components import hassio
|
||||
from homeassistant.components.api import ATTR_INSTALLATION_TYPE
|
||||
from homeassistant.components.automation.const import DOMAIN as AUTOMATION_DOMAIN
|
||||
from homeassistant.components.energy import (
|
||||
DOMAIN as ENERGY_DOMAIN,
|
||||
is_configured as energy_is_configured,
|
||||
)
|
||||
from homeassistant.const import ATTR_DOMAIN, __version__ as HA_VERSION
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
@@ -26,8 +30,10 @@ from .const import (
|
||||
ATTR_AUTOMATION_COUNT,
|
||||
ATTR_BASE,
|
||||
ATTR_BOARD,
|
||||
ATTR_CONFIGURED,
|
||||
ATTR_CUSTOM_INTEGRATIONS,
|
||||
ATTR_DIAGNOSTICS,
|
||||
ATTR_ENERGY,
|
||||
ATTR_HEALTHY,
|
||||
ATTR_INTEGRATION_COUNT,
|
||||
ATTR_INTEGRATIONS,
|
||||
@@ -222,6 +228,11 @@ class Analytics:
|
||||
if supervisor_info is not None:
|
||||
payload[ATTR_ADDONS] = addons
|
||||
|
||||
if ENERGY_DOMAIN in integrations:
|
||||
payload[ATTR_ENERGY] = {
|
||||
ATTR_CONFIGURED: await energy_is_configured(self.hass)
|
||||
}
|
||||
|
||||
if self.preferences.get(ATTR_STATISTICS, False):
|
||||
payload[ATTR_STATE_COUNT] = len(self.hass.states.async_all())
|
||||
payload[ATTR_AUTOMATION_COUNT] = len(
|
||||
|
||||
@@ -21,8 +21,10 @@ ATTR_AUTO_UPDATE = "auto_update"
|
||||
ATTR_AUTOMATION_COUNT = "automation_count"
|
||||
ATTR_BASE = "base"
|
||||
ATTR_BOARD = "board"
|
||||
ATTR_CONFIGURED = "configured"
|
||||
ATTR_CUSTOM_INTEGRATIONS = "custom_integrations"
|
||||
ATTR_DIAGNOSTICS = "diagnostics"
|
||||
ATTR_ENERGY = "energy"
|
||||
ATTR_HEALTHY = "healthy"
|
||||
ATTR_INSTALLATION_TYPE = "installation_type"
|
||||
ATTR_INTEGRATION_COUNT = "integration_count"
|
||||
|
||||
@@ -2,8 +2,17 @@
|
||||
"domain": "analytics",
|
||||
"name": "Analytics",
|
||||
"documentation": "https://www.home-assistant.io/integrations/analytics",
|
||||
"codeowners": ["@home-assistant/core", "@ludeeus"],
|
||||
"dependencies": ["api", "websocket_api"],
|
||||
"codeowners": [
|
||||
"@home-assistant/core",
|
||||
"@ludeeus"
|
||||
],
|
||||
"dependencies": [
|
||||
"api",
|
||||
"websocket_api"
|
||||
],
|
||||
"after_dependencies": [
|
||||
"energy"
|
||||
],
|
||||
"quality_scale": "internal",
|
||||
"iot_class": "cloud_push"
|
||||
}
|
||||
}
|
||||
@@ -449,6 +449,11 @@ class ADBDevice(MediaPlayerEntity):
|
||||
ATTR_HDMI_INPUT: None,
|
||||
}
|
||||
|
||||
@property
|
||||
def media_image_hash(self):
|
||||
"""Hash value for media image."""
|
||||
return f"{datetime.now().timestamp()}" if self._screencap else None
|
||||
|
||||
@adb_decorator()
|
||||
async def _adb_screencap(self):
|
||||
"""Take a screen capture from the device."""
|
||||
@@ -458,9 +463,6 @@ class ADBDevice(MediaPlayerEntity):
|
||||
"""Fetch current playing image."""
|
||||
if not self._screencap or self.state in [STATE_OFF, None] or not self.available:
|
||||
return None, None
|
||||
self._attr_media_image_hash = (
|
||||
f"{datetime.now().timestamp()}" if self._screencap else None
|
||||
)
|
||||
|
||||
media_data = await self._adb_screencap()
|
||||
if media_data:
|
||||
|
||||
@@ -26,72 +26,72 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SENSOR_PREFIX = "UPS "
|
||||
SENSOR_TYPES = {
|
||||
"alarmdel": ["Alarm Delay", "", "mdi:alarm", None],
|
||||
"ambtemp": ["Ambient Temperature", "", "mdi:thermometer", None],
|
||||
"apc": ["Status Data", "", "mdi:information-outline", None],
|
||||
"apcmodel": ["Model", "", "mdi:information-outline", None],
|
||||
"badbatts": ["Bad Batteries", "", "mdi:information-outline", None],
|
||||
"battdate": ["Battery Replaced", "", "mdi:calendar-clock", None],
|
||||
"battstat": ["Battery Status", "", "mdi:information-outline", None],
|
||||
"alarmdel": ["Alarm Delay", None, "mdi:alarm", None],
|
||||
"ambtemp": ["Ambient Temperature", None, "mdi:thermometer", None],
|
||||
"apc": ["Status Data", None, "mdi:information-outline", None],
|
||||
"apcmodel": ["Model", None, "mdi:information-outline", None],
|
||||
"badbatts": ["Bad Batteries", None, "mdi:information-outline", None],
|
||||
"battdate": ["Battery Replaced", None, "mdi:calendar-clock", None],
|
||||
"battstat": ["Battery Status", None, "mdi:information-outline", None],
|
||||
"battv": ["Battery Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"bcharge": ["Battery", PERCENTAGE, "mdi:battery", None],
|
||||
"cable": ["Cable Type", "", "mdi:ethernet-cable", None],
|
||||
"cumonbatt": ["Total Time on Battery", "", "mdi:timer-outline", None],
|
||||
"date": ["Status Date", "", "mdi:calendar-clock", None],
|
||||
"dipsw": ["Dip Switch Settings", "", "mdi:information-outline", None],
|
||||
"dlowbatt": ["Low Battery Signal", "", "mdi:clock-alert", None],
|
||||
"driver": ["Driver", "", "mdi:information-outline", None],
|
||||
"dshutd": ["Shutdown Delay", "", "mdi:timer-outline", None],
|
||||
"dwake": ["Wake Delay", "", "mdi:timer-outline", None],
|
||||
"endapc": ["Date and Time", "", "mdi:calendar-clock", None],
|
||||
"extbatts": ["External Batteries", "", "mdi:information-outline", None],
|
||||
"firmware": ["Firmware Version", "", "mdi:information-outline", None],
|
||||
"cable": ["Cable Type", None, "mdi:ethernet-cable", None],
|
||||
"cumonbatt": ["Total Time on Battery", None, "mdi:timer-outline", None],
|
||||
"date": ["Status Date", None, "mdi:calendar-clock", None],
|
||||
"dipsw": ["Dip Switch Settings", None, "mdi:information-outline", None],
|
||||
"dlowbatt": ["Low Battery Signal", None, "mdi:clock-alert", None],
|
||||
"driver": ["Driver", None, "mdi:information-outline", None],
|
||||
"dshutd": ["Shutdown Delay", None, "mdi:timer-outline", None],
|
||||
"dwake": ["Wake Delay", None, "mdi:timer-outline", None],
|
||||
"endapc": ["Date and Time", None, "mdi:calendar-clock", None],
|
||||
"extbatts": ["External Batteries", None, "mdi:information-outline", None],
|
||||
"firmware": ["Firmware Version", None, "mdi:information-outline", None],
|
||||
"hitrans": ["Transfer High", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"hostname": ["Hostname", "", "mdi:information-outline", None],
|
||||
"hostname": ["Hostname", None, "mdi:information-outline", None],
|
||||
"humidity": ["Ambient Humidity", PERCENTAGE, "mdi:water-percent", None],
|
||||
"itemp": ["Internal Temperature", TEMP_CELSIUS, None, DEVICE_CLASS_TEMPERATURE],
|
||||
"lastxfer": ["Last Transfer", "", "mdi:transfer", None],
|
||||
"linefail": ["Input Voltage Status", "", "mdi:information-outline", None],
|
||||
"lastxfer": ["Last Transfer", None, "mdi:transfer", None],
|
||||
"linefail": ["Input Voltage Status", None, "mdi:information-outline", None],
|
||||
"linefreq": ["Line Frequency", FREQUENCY_HERTZ, "mdi:information-outline", None],
|
||||
"linev": ["Input Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"loadpct": ["Load", PERCENTAGE, "mdi:gauge", None],
|
||||
"loadapnt": ["Load Apparent Power", PERCENTAGE, "mdi:gauge", None],
|
||||
"lotrans": ["Transfer Low", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"mandate": ["Manufacture Date", "", "mdi:calendar", None],
|
||||
"masterupd": ["Master Update", "", "mdi:information-outline", None],
|
||||
"mandate": ["Manufacture Date", None, "mdi:calendar", None],
|
||||
"masterupd": ["Master Update", None, "mdi:information-outline", None],
|
||||
"maxlinev": ["Input Voltage High", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"maxtime": ["Battery Timeout", "", "mdi:timer-off-outline", None],
|
||||
"maxtime": ["Battery Timeout", None, "mdi:timer-off-outline", None],
|
||||
"mbattchg": ["Battery Shutdown", PERCENTAGE, "mdi:battery-alert", None],
|
||||
"minlinev": ["Input Voltage Low", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"mintimel": ["Shutdown Time", "", "mdi:timer-outline", None],
|
||||
"model": ["Model", "", "mdi:information-outline", None],
|
||||
"mintimel": ["Shutdown Time", None, "mdi:timer-outline", None],
|
||||
"model": ["Model", None, "mdi:information-outline", None],
|
||||
"nombattv": ["Battery Nominal Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"nominv": ["Nominal Input Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"nomoutv": ["Nominal Output Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"nompower": ["Nominal Output Power", POWER_WATT, "mdi:flash", None],
|
||||
"nomapnt": ["Nominal Apparent Power", POWER_VOLT_AMPERE, "mdi:flash", None],
|
||||
"numxfers": ["Transfer Count", "", "mdi:counter", None],
|
||||
"numxfers": ["Transfer Count", None, "mdi:counter", None],
|
||||
"outcurnt": ["Output Current", ELECTRIC_CURRENT_AMPERE, "mdi:flash", None],
|
||||
"outputv": ["Output Voltage", ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None],
|
||||
"reg1": ["Register 1 Fault", "", "mdi:information-outline", None],
|
||||
"reg2": ["Register 2 Fault", "", "mdi:information-outline", None],
|
||||
"reg3": ["Register 3 Fault", "", "mdi:information-outline", None],
|
||||
"reg1": ["Register 1 Fault", None, "mdi:information-outline", None],
|
||||
"reg2": ["Register 2 Fault", None, "mdi:information-outline", None],
|
||||
"reg3": ["Register 3 Fault", None, "mdi:information-outline", None],
|
||||
"retpct": ["Restore Requirement", PERCENTAGE, "mdi:battery-alert", None],
|
||||
"selftest": ["Last Self Test", "", "mdi:calendar-clock", None],
|
||||
"sense": ["Sensitivity", "", "mdi:information-outline", None],
|
||||
"serialno": ["Serial Number", "", "mdi:information-outline", None],
|
||||
"starttime": ["Startup Time", "", "mdi:calendar-clock", None],
|
||||
"statflag": ["Status Flag", "", "mdi:information-outline", None],
|
||||
"status": ["Status", "", "mdi:information-outline", None],
|
||||
"stesti": ["Self Test Interval", "", "mdi:information-outline", None],
|
||||
"timeleft": ["Time Left", "", "mdi:clock-alert", None],
|
||||
"tonbatt": ["Time on Battery", "", "mdi:timer-outline", None],
|
||||
"upsmode": ["Mode", "", "mdi:information-outline", None],
|
||||
"upsname": ["Name", "", "mdi:information-outline", None],
|
||||
"version": ["Daemon Info", "", "mdi:information-outline", None],
|
||||
"xoffbat": ["Transfer from Battery", "", "mdi:transfer", None],
|
||||
"xoffbatt": ["Transfer from Battery", "", "mdi:transfer", None],
|
||||
"xonbatt": ["Transfer to Battery", "", "mdi:transfer", None],
|
||||
"selftest": ["Last Self Test", None, "mdi:calendar-clock", None],
|
||||
"sense": ["Sensitivity", None, "mdi:information-outline", None],
|
||||
"serialno": ["Serial Number", None, "mdi:information-outline", None],
|
||||
"starttime": ["Startup Time", None, "mdi:calendar-clock", None],
|
||||
"statflag": ["Status Flag", None, "mdi:information-outline", None],
|
||||
"status": ["Status", None, "mdi:information-outline", None],
|
||||
"stesti": ["Self Test Interval", None, "mdi:information-outline", None],
|
||||
"timeleft": ["Time Left", None, "mdi:clock-alert", None],
|
||||
"tonbatt": ["Time on Battery", None, "mdi:timer-outline", None],
|
||||
"upsmode": ["Mode", None, "mdi:information-outline", None],
|
||||
"upsname": ["Name", None, "mdi:information-outline", None],
|
||||
"version": ["Daemon Info", None, "mdi:information-outline", None],
|
||||
"xoffbat": ["Transfer from Battery", None, "mdi:transfer", None],
|
||||
"xoffbatt": ["Transfer from Battery", None, "mdi:transfer", None],
|
||||
"xonbatt": ["Transfer to Battery", None, "mdi:transfer", None],
|
||||
}
|
||||
|
||||
SPECIFIC_UNITS = {"ITEMP": TEMP_CELSIUS}
|
||||
@@ -165,8 +165,7 @@ class APCUPSdSensor(SensorEntity):
|
||||
self.type = sensor_type
|
||||
self._attr_name = SENSOR_PREFIX + SENSOR_TYPES[sensor_type][0]
|
||||
self._attr_icon = SENSOR_TYPES[self.type][2]
|
||||
if SENSOR_TYPES[sensor_type][1]:
|
||||
self._attr_unit_of_measurement = SENSOR_TYPES[sensor_type][1]
|
||||
self._attr_unit_of_measurement = SENSOR_TYPES[sensor_type][1]
|
||||
self._attr_device_class = SENSOR_TYPES[sensor_type][3]
|
||||
|
||||
def update(self):
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Apple TV",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/apple_tv",
|
||||
"requirements": ["pyatv==0.8.1"],
|
||||
"requirements": ["pyatv==0.8.2"],
|
||||
"zeroconf": ["_mediaremotetv._tcp.local.", "_touch-able._tcp.local."],
|
||||
"after_dependencies": ["discovery"],
|
||||
"codeowners": ["@postlund"],
|
||||
|
||||
@@ -93,9 +93,10 @@ class AquaLogicSensor(SensorEntity):
|
||||
if panel is not None:
|
||||
if panel.is_metric:
|
||||
self._attr_unit_of_measurement = SENSOR_TYPES[self._type][1][0]
|
||||
self._attr_state = getattr(panel, self._type)
|
||||
self.async_write_ha_state()
|
||||
else:
|
||||
self._attr_unit_of_measurement = SENSOR_TYPES[self._type][1][1]
|
||||
|
||||
self._attr_state = getattr(panel, self._type)
|
||||
self.async_write_ha_state()
|
||||
else:
|
||||
self._attr_unit_of_measurement = None
|
||||
|
||||
@@ -5,10 +5,16 @@
|
||||
"already_in_progress": "\u05d6\u05e8\u05d9\u05de\u05ea \u05d4\u05ea\u05e6\u05d5\u05e8\u05d4 \u05db\u05d1\u05e8 \u05de\u05ea\u05d1\u05e6\u05e2\u05ea",
|
||||
"cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4"
|
||||
},
|
||||
"error": {
|
||||
"many": "\u05e8\u05d9\u05e7\u05d9\u05dd",
|
||||
"one": "\u05e8\u05d9\u05e7",
|
||||
"other": "\u05e8\u05d9\u05e7\u05d9\u05dd",
|
||||
"two": "\u05e8\u05d9\u05e7\u05d9\u05dd"
|
||||
},
|
||||
"flow_title": "{host}",
|
||||
"step": {
|
||||
"confirm": {
|
||||
"description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d5\u05e1\u05d9\u05e3 \u05d0\u05ea Arcam FMJ \u05d1- '{host}' \u05dc-Home Assistant?"
|
||||
"description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d5\u05e1\u05d9\u05e3 \u05d0\u05ea Arcam FMJ \u05d1-`{host}` \u05dc-Home Assistant?"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"username": "Benutzername"
|
||||
},
|
||||
"description": "Einstellen der erforderlichen Parameter f\u00fcr die Verbindung mit deinem Router.",
|
||||
"title": ""
|
||||
"title": "AsusWRT"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -14,12 +14,13 @@ from homeassistant.const import (
|
||||
CONF_NAME,
|
||||
CONF_PASSWORD,
|
||||
CONF_USERNAME,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_POWER,
|
||||
ENERGY_KILO_WATT_HOUR,
|
||||
POWER_WATT,
|
||||
)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.util import Throttle
|
||||
from homeassistant.util import Throttle, dt as dt_util
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -87,12 +88,16 @@ class AtomeData:
|
||||
self._is_connected = None
|
||||
self._day_usage = None
|
||||
self._day_price = None
|
||||
self._day_last_reset = None
|
||||
self._week_usage = None
|
||||
self._week_price = None
|
||||
self._week_last_reset = None
|
||||
self._month_usage = None
|
||||
self._month_price = None
|
||||
self._month_last_reset = None
|
||||
self._year_usage = None
|
||||
self._year_price = None
|
||||
self._year_last_reset = None
|
||||
|
||||
@property
|
||||
def live_power(self):
|
||||
@@ -137,6 +142,11 @@ class AtomeData:
|
||||
"""Return latest daily usage value."""
|
||||
return self._day_price
|
||||
|
||||
@property
|
||||
def day_last_reset(self):
|
||||
"""Return latest daily last reset."""
|
||||
return self._day_last_reset
|
||||
|
||||
@Throttle(DAILY_SCAN_INTERVAL)
|
||||
def update_day_usage(self):
|
||||
"""Return current daily power usage."""
|
||||
@@ -144,6 +154,7 @@ class AtomeData:
|
||||
values = self.atome_client.get_consumption(DAILY_TYPE)
|
||||
self._day_usage = values["total"] / 1000
|
||||
self._day_price = values["price"]
|
||||
self._day_last_reset = dt_util.parse_datetime(values["startPeriod"])
|
||||
_LOGGER.debug("Updating Atome daily data. Got: %d", self._day_usage)
|
||||
|
||||
except KeyError as error:
|
||||
@@ -159,6 +170,11 @@ class AtomeData:
|
||||
"""Return latest weekly usage value."""
|
||||
return self._week_price
|
||||
|
||||
@property
|
||||
def week_last_reset(self):
|
||||
"""Return latest weekly last reset value."""
|
||||
return self._week_last_reset
|
||||
|
||||
@Throttle(WEEKLY_SCAN_INTERVAL)
|
||||
def update_week_usage(self):
|
||||
"""Return current weekly power usage."""
|
||||
@@ -166,6 +182,7 @@ class AtomeData:
|
||||
values = self.atome_client.get_consumption(WEEKLY_TYPE)
|
||||
self._week_usage = values["total"] / 1000
|
||||
self._week_price = values["price"]
|
||||
self._week_last_reset = dt_util.parse_datetime(values["startPeriod"])
|
||||
_LOGGER.debug("Updating Atome weekly data. Got: %d", self._week_usage)
|
||||
|
||||
except KeyError as error:
|
||||
@@ -181,6 +198,11 @@ class AtomeData:
|
||||
"""Return latest monthly usage value."""
|
||||
return self._month_price
|
||||
|
||||
@property
|
||||
def month_last_reset(self):
|
||||
"""Return latest monthly last reset value."""
|
||||
return self._month_last_reset
|
||||
|
||||
@Throttle(MONTHLY_SCAN_INTERVAL)
|
||||
def update_month_usage(self):
|
||||
"""Return current monthly power usage."""
|
||||
@@ -188,6 +210,7 @@ class AtomeData:
|
||||
values = self.atome_client.get_consumption(MONTHLY_TYPE)
|
||||
self._month_usage = values["total"] / 1000
|
||||
self._month_price = values["price"]
|
||||
self._month_last_reset = dt_util.parse_datetime(values["startPeriod"])
|
||||
_LOGGER.debug("Updating Atome monthly data. Got: %d", self._month_usage)
|
||||
|
||||
except KeyError as error:
|
||||
@@ -203,6 +226,11 @@ class AtomeData:
|
||||
"""Return latest yearly usage value."""
|
||||
return self._year_price
|
||||
|
||||
@property
|
||||
def year_last_reset(self):
|
||||
"""Return latest yearly last reset value."""
|
||||
return self._year_last_reset
|
||||
|
||||
@Throttle(YEARLY_SCAN_INTERVAL)
|
||||
def update_year_usage(self):
|
||||
"""Return current yearly power usage."""
|
||||
@@ -210,6 +238,7 @@ class AtomeData:
|
||||
values = self.atome_client.get_consumption(YEARLY_TYPE)
|
||||
self._year_usage = values["total"] / 1000
|
||||
self._year_price = values["price"]
|
||||
self._year_last_reset = dt_util.parse_datetime(values["startPeriod"])
|
||||
_LOGGER.debug("Updating Atome yearly data. Got: %d", self._year_usage)
|
||||
|
||||
except KeyError as error:
|
||||
@@ -219,19 +248,19 @@ class AtomeData:
|
||||
class AtomeSensor(SensorEntity):
|
||||
"""Representation of a sensor entity for Atome."""
|
||||
|
||||
_attr_device_class = DEVICE_CLASS_POWER
|
||||
|
||||
def __init__(self, data, name, sensor_type):
|
||||
"""Initialize the sensor."""
|
||||
self._attr_name = name
|
||||
self._data = data
|
||||
|
||||
self._sensor_type = sensor_type
|
||||
self._attr_state_class = STATE_CLASS_MEASUREMENT
|
||||
|
||||
if sensor_type == LIVE_TYPE:
|
||||
self._attr_device_class = DEVICE_CLASS_POWER
|
||||
self._attr_unit_of_measurement = POWER_WATT
|
||||
self._attr_state_class = STATE_CLASS_MEASUREMENT
|
||||
else:
|
||||
self._attr_device_class = DEVICE_CLASS_ENERGY
|
||||
self._attr_unit_of_measurement = ENERGY_KILO_WATT_HOUR
|
||||
|
||||
def update(self):
|
||||
@@ -247,6 +276,9 @@ class AtomeSensor(SensorEntity):
|
||||
}
|
||||
else:
|
||||
self._attr_state = getattr(self._data, f"{self._sensor_type}_usage")
|
||||
self._attr_last_reset = dt_util.as_utc(
|
||||
getattr(self._data, f"{self._sensor_type}_last_reset")
|
||||
)
|
||||
self._attr_extra_state_attributes = {
|
||||
"price": getattr(self._data, f"{self._sensor_type}_price")
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
}
|
||||
},
|
||||
"validation": {
|
||||
"description": "\u05d0\u05e0\u05d0 \u05d1\u05d3\u05d5\u05e7 \u05d0\u05ea {login_method} \u05e9\u05dc\u05da ({\u05e9\u05dd \u05de\u05e9\u05ea\u05de\u05e9}) \u05d5\u05d4\u05d6\u05df \u05d0\u05ea \u05e7\u05d5\u05d3 \u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05e9\u05dc\u05d4\u05dc\u05df"
|
||||
"description": "\u05e0\u05d0 \u05dc\u05d1\u05d3\u05d5\u05e7 \u05d0\u05ea {login_method} ( {username} ) \u05d5\u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05ea \u05e7\u05d5\u05d3 \u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05e9\u05dc\u05d4\u05dc\u05df"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
"mfa_setup": {
|
||||
"notify": {
|
||||
"abort": {
|
||||
"no_available_service": "\u05d0\u05d9\u05df \u05e9\u05d9\u05e8\u05d5\u05ea\u05d9 notify \u05d6\u05de\u05d9\u05e0\u05d9\u05dd."
|
||||
"no_available_service": "\u05d0\u05d9\u05df \u05e9\u05d9\u05e8\u05d5\u05ea\u05d9 \u05d4\u05ea\u05e8\u05d0\u05d5\u05ea \u05d6\u05de\u05d9\u05e0\u05d9\u05dd."
|
||||
},
|
||||
"error": {
|
||||
"invalid_code": "\u05e7\u05d5\u05d3 \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9, \u05d0\u05e0\u05d0 \u05e0\u05e1\u05d4 \u05e9\u05d5\u05d1."
|
||||
},
|
||||
"step": {
|
||||
"init": {
|
||||
"description": "\u05d1\u05d7\u05e8 \u05d0\u05ea \u05d0\u05d7\u05d3 \u05de\u05e9\u05e8\u05d5\u05ea\u05d9 notify",
|
||||
"description": "\u05e0\u05d0 \u05dc\u05d1\u05d7\u05d5\u05e8 \u05d0\u05d7\u05d3 \u05de\u05e9\u05d9\u05e8\u05d5\u05ea\u05d9 \u05d4\u05d4\u05d5\u05d3\u05e2\u05d5\u05ea:",
|
||||
"title": "\u05d4\u05d2\u05d3\u05e8 \u05e1\u05d9\u05e1\u05de\u05d4 \u05d7\u05d3 \u05e4\u05e2\u05de\u05d9\u05ea \u05d4\u05e0\u05e9\u05dc\u05d7\u05ea \u05e2\u05dc \u05d9\u05d3\u05d9 \u05e8\u05db\u05d9\u05d1 notify"
|
||||
},
|
||||
"setup": {
|
||||
"description": "\u05e1\u05d9\u05e1\u05de\u05d4 \u05d7\u05d3 \u05e4\u05e2\u05de\u05d9\u05ea \u05e0\u05e9\u05dc\u05d7\u05d4 \u05e2\u05dc \u05d9\u05d3\u05d9 **{notify_service}**. \u05d4\u05d6\u05df \u05d0\u05d5\u05ea\u05d4 \u05dc\u05de\u05d8\u05d4:",
|
||||
"description": "\u05e1\u05d9\u05e1\u05de\u05d4 \u05d7\u05d3 \u05e4\u05e2\u05de\u05d9\u05ea \u05e0\u05e9\u05dc\u05d7\u05d4 \u05d1\u05d0\u05de\u05e6\u05e2\u05d5\u05ea ** Notify. {notify_service} **. \u05e0\u05d0 \u05dc\u05d4\u05d6\u05d9\u05df \u05d0\u05d5\u05ea\u05d5 \u05dc\u05de\u05d8\u05d4:",
|
||||
"title": "\u05d0\u05d9\u05de\u05d5\u05ea \u05d4\u05d4\u05ea\u05e7\u05e0\u05d4"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
"""The Automate Pulse Hub v2 integration."""
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import DOMAIN
|
||||
from .hub import PulseHub
|
||||
|
||||
PLATFORMS = ["cover"]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Automate Pulse Hub v2 from a config entry."""
|
||||
hub = PulseHub(hass, entry)
|
||||
|
||||
if not await hub.async_setup():
|
||||
return False
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = hub
|
||||
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
hub = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
if not await hub.async_reset():
|
||||
return False
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
@@ -1,93 +0,0 @@
|
||||
"""Base class for Automate Roller Blinds."""
|
||||
import logging
|
||||
|
||||
import aiopulse2
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import entity
|
||||
from homeassistant.helpers.device_registry import async_get_registry as get_dev_reg
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_registry import async_get_registry as get_ent_reg
|
||||
|
||||
from .const import AUTOMATE_ENTITY_REMOVE, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AutomateBase(entity.Entity):
|
||||
"""Base representation of an Automate roller."""
|
||||
|
||||
def __init__(self, roller: aiopulse2.Roller) -> None:
|
||||
"""Initialize the roller."""
|
||||
self.roller = roller
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if roller and hub is available."""
|
||||
return self.roller.online and self.roller.hub.connected
|
||||
|
||||
async def async_remove_and_unregister(self):
|
||||
"""Unregister from entity and device registry and call entity remove function."""
|
||||
_LOGGER.info("Removing %s %s", self.__class__.__name__, self.unique_id)
|
||||
|
||||
ent_registry = await get_ent_reg(self.hass)
|
||||
if self.entity_id in ent_registry.entities:
|
||||
ent_registry.async_remove(self.entity_id)
|
||||
|
||||
dev_registry = await get_dev_reg(self.hass)
|
||||
device = dev_registry.async_get_device(
|
||||
identifiers={(DOMAIN, self.unique_id)}, connections=set()
|
||||
)
|
||||
if device is not None:
|
||||
dev_registry.async_update_device(
|
||||
device.id, remove_config_entry_id=self.registry_entry.config_entry_id
|
||||
)
|
||||
|
||||
await self.async_remove()
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Entity has been added to hass."""
|
||||
self.roller.callback_subscribe(self.notify_update)
|
||||
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
AUTOMATE_ENTITY_REMOVE.format(self.roller.id),
|
||||
self.async_remove_and_unregister,
|
||||
)
|
||||
)
|
||||
|
||||
async def async_will_remove_from_hass(self):
|
||||
"""Entity being removed from hass."""
|
||||
self.roller.callback_unsubscribe(self.notify_update)
|
||||
|
||||
@callback
|
||||
def notify_update(self, roller: aiopulse2.Roller):
|
||||
"""Write updated device state information."""
|
||||
_LOGGER.debug(
|
||||
"Device update notification received: %s (%r)", roller.id, roller.name
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Report that Automate entities do not need polling."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the unique ID of this roller."""
|
||||
return self.roller.id
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of roller."""
|
||||
return self.roller.name
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return the device info."""
|
||||
attrs = {
|
||||
"identifiers": {(DOMAIN, self.roller.id)},
|
||||
}
|
||||
return attrs
|
||||
@@ -1,37 +0,0 @@
|
||||
"""Config flow for Automate Pulse Hub v2 integration."""
|
||||
import logging
|
||||
|
||||
import aiopulse2
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DATA_SCHEMA = vol.Schema({vol.Required("host"): str})
|
||||
|
||||
|
||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Automate Pulse Hub v2."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle the initial step once we have info from the user."""
|
||||
if user_input is not None:
|
||||
try:
|
||||
hub = aiopulse2.Hub(user_input["host"])
|
||||
await hub.test()
|
||||
title = hub.name
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=DATA_SCHEMA,
|
||||
errors={"base": "cannot_connect"},
|
||||
)
|
||||
|
||||
return self.async_create_entry(title=title, data=user_input)
|
||||
|
||||
return self.async_show_form(step_id="user", data_schema=DATA_SCHEMA)
|
||||
@@ -1,6 +0,0 @@
|
||||
"""Constants for the Automate Pulse Hub v2 integration."""
|
||||
|
||||
DOMAIN = "automate"
|
||||
|
||||
AUTOMATE_HUB_UPDATE = "automate_hub_update_{}"
|
||||
AUTOMATE_ENTITY_REMOVE = "automate_entity_remove_{}"
|
||||
@@ -1,147 +0,0 @@
|
||||
"""Support for Automate Roller Blinds."""
|
||||
import aiopulse2
|
||||
|
||||
from homeassistant.components.cover import (
|
||||
ATTR_POSITION,
|
||||
DEVICE_CLASS_SHADE,
|
||||
SUPPORT_CLOSE,
|
||||
SUPPORT_CLOSE_TILT,
|
||||
SUPPORT_OPEN,
|
||||
SUPPORT_OPEN_TILT,
|
||||
SUPPORT_SET_POSITION,
|
||||
SUPPORT_SET_TILT_POSITION,
|
||||
SUPPORT_STOP,
|
||||
SUPPORT_STOP_TILT,
|
||||
CoverEntity,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from .base import AutomateBase
|
||||
from .const import AUTOMATE_HUB_UPDATE, DOMAIN
|
||||
from .helpers import async_add_automate_entities
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Automate Rollers from a config entry."""
|
||||
hub = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
current = set()
|
||||
|
||||
@callback
|
||||
def async_add_automate_covers():
|
||||
async_add_automate_entities(
|
||||
hass, AutomateCover, config_entry, current, async_add_entities
|
||||
)
|
||||
|
||||
hub.cleanup_callbacks.append(
|
||||
async_dispatcher_connect(
|
||||
hass,
|
||||
AUTOMATE_HUB_UPDATE.format(config_entry.entry_id),
|
||||
async_add_automate_covers,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class AutomateCover(AutomateBase, CoverEntity):
|
||||
"""Representation of a Automate cover device."""
|
||||
|
||||
@property
|
||||
def current_cover_position(self):
|
||||
"""Return the current position of the roller blind.
|
||||
|
||||
None is unknown, 0 is closed, 100 is fully open.
|
||||
"""
|
||||
position = None
|
||||
if self.roller.closed_percent is not None:
|
||||
position = 100 - self.roller.closed_percent
|
||||
return position
|
||||
|
||||
@property
|
||||
def current_cover_tilt_position(self):
|
||||
"""Return the current tilt of the roller blind.
|
||||
|
||||
None is unknown, 0 is closed, 100 is fully open.
|
||||
"""
|
||||
return None
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag supported features."""
|
||||
supported_features = 0
|
||||
if self.current_cover_position is not None:
|
||||
supported_features |= (
|
||||
SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP | SUPPORT_SET_POSITION
|
||||
)
|
||||
if self.current_cover_tilt_position is not None:
|
||||
supported_features |= (
|
||||
SUPPORT_OPEN_TILT
|
||||
| SUPPORT_CLOSE_TILT
|
||||
| SUPPORT_STOP_TILT
|
||||
| SUPPORT_SET_TILT_POSITION
|
||||
)
|
||||
|
||||
return supported_features
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return the device info."""
|
||||
attrs = super().device_info
|
||||
attrs["manufacturer"] = "Automate"
|
||||
attrs["model"] = self.roller.devicetype
|
||||
attrs["sw_version"] = self.roller.version
|
||||
attrs["via_device"] = (DOMAIN, self.roller.hub.id)
|
||||
attrs["name"] = self.name
|
||||
return attrs
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Class of the cover, a shade."""
|
||||
return DEVICE_CLASS_SHADE
|
||||
|
||||
@property
|
||||
def is_opening(self):
|
||||
"""Is cover opening/moving up."""
|
||||
return self.roller.action == aiopulse2.MovingAction.up
|
||||
|
||||
@property
|
||||
def is_closing(self):
|
||||
"""Is cover closing/moving down."""
|
||||
return self.roller.action == aiopulse2.MovingAction.down
|
||||
|
||||
@property
|
||||
def is_closed(self):
|
||||
"""Return if the cover is closed."""
|
||||
return self.roller.closed_percent == 100
|
||||
|
||||
async def async_close_cover(self, **kwargs):
|
||||
"""Close the roller."""
|
||||
await self.roller.move_down()
|
||||
|
||||
async def async_open_cover(self, **kwargs):
|
||||
"""Open the roller."""
|
||||
await self.roller.move_up()
|
||||
|
||||
async def async_stop_cover(self, **kwargs):
|
||||
"""Stop the roller."""
|
||||
await self.roller.move_stop()
|
||||
|
||||
async def async_set_cover_position(self, **kwargs):
|
||||
"""Move the roller shutter to a specific position."""
|
||||
await self.roller.move_to(100 - kwargs[ATTR_POSITION])
|
||||
|
||||
async def async_close_cover_tilt(self, **kwargs):
|
||||
"""Close the roller."""
|
||||
await self.roller.move_down()
|
||||
|
||||
async def async_open_cover_tilt(self, **kwargs):
|
||||
"""Open the roller."""
|
||||
await self.roller.move_up()
|
||||
|
||||
async def async_stop_cover_tilt(self, **kwargs):
|
||||
"""Stop the roller."""
|
||||
await self.roller.move_stop()
|
||||
|
||||
async def async_set_cover_tilt(self, **kwargs):
|
||||
"""Tilt the roller shutter to a specific position."""
|
||||
await self.roller.move_to(100 - kwargs[ATTR_POSITION])
|
||||
@@ -1,46 +0,0 @@
|
||||
"""Helper functions for Automate Pulse."""
|
||||
import logging
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.device_registry import async_get_registry as get_dev_reg
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@callback
|
||||
def async_add_automate_entities(
|
||||
hass, entity_class, config_entry, current, async_add_entities
|
||||
):
|
||||
"""Add any new entities."""
|
||||
hub = hass.data[DOMAIN][config_entry.entry_id]
|
||||
_LOGGER.debug("Looking for new %s on: %s", entity_class.__name__, hub.host)
|
||||
|
||||
api = hub.api.rollers
|
||||
|
||||
new_items = []
|
||||
for unique_id, roller in api.items():
|
||||
if unique_id not in current:
|
||||
_LOGGER.debug("New %s %s", entity_class.__name__, unique_id)
|
||||
new_item = entity_class(roller)
|
||||
current.add(unique_id)
|
||||
new_items.append(new_item)
|
||||
|
||||
async_add_entities(new_items)
|
||||
|
||||
|
||||
async def update_devices(hass, config_entry, api):
|
||||
"""Tell hass that device info has been updated."""
|
||||
dev_registry = await get_dev_reg(hass)
|
||||
|
||||
for api_item in api.values():
|
||||
# Update Device name
|
||||
device = dev_registry.async_get_device(
|
||||
identifiers={(DOMAIN, api_item.id)}, connections=set()
|
||||
)
|
||||
if device is not None:
|
||||
dev_registry.async_update_device(
|
||||
device.id,
|
||||
name=api_item.name,
|
||||
)
|
||||
@@ -1,89 +0,0 @@
|
||||
"""Code to handle a Pulse Hub."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import aiopulse2
|
||||
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
|
||||
from .const import AUTOMATE_ENTITY_REMOVE, AUTOMATE_HUB_UPDATE
|
||||
from .helpers import update_devices
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PulseHub:
|
||||
"""Manages a single Pulse Hub."""
|
||||
|
||||
def __init__(self, hass, config_entry):
|
||||
"""Initialize the system."""
|
||||
self.config_entry = config_entry
|
||||
self.hass = hass
|
||||
self.api: aiopulse2.Hub | None = None
|
||||
self.tasks = []
|
||||
self.current_rollers = {}
|
||||
self.cleanup_callbacks = []
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
"""Return the title of the hub shown in the integrations list."""
|
||||
return f"{self.api.name} ({self.api.host})"
|
||||
|
||||
@property
|
||||
def host(self):
|
||||
"""Return the host of this hub."""
|
||||
return self.config_entry.data["host"]
|
||||
|
||||
async def async_setup(self):
|
||||
"""Set up a hub based on host parameter."""
|
||||
host = self.host
|
||||
|
||||
hub = aiopulse2.Hub(host, propagate_callbacks=True)
|
||||
|
||||
self.api = hub
|
||||
|
||||
hub.callback_subscribe(self.async_notify_update)
|
||||
self.tasks.append(asyncio.create_task(hub.run()))
|
||||
|
||||
_LOGGER.debug("Hub setup complete")
|
||||
return True
|
||||
|
||||
async def async_reset(self):
|
||||
"""Reset this hub to default state."""
|
||||
for cleanup_callback in self.cleanup_callbacks:
|
||||
cleanup_callback()
|
||||
|
||||
# If not setup
|
||||
if self.api is None:
|
||||
return False
|
||||
|
||||
self.api.callback_unsubscribe(self.async_notify_update)
|
||||
await self.api.stop()
|
||||
del self.api
|
||||
self.api = None
|
||||
|
||||
# Wait for any running tasks to complete
|
||||
await asyncio.wait(self.tasks)
|
||||
|
||||
return True
|
||||
|
||||
async def async_notify_update(self, hub=None):
|
||||
"""Evaluate entities when hub reports that update has occurred."""
|
||||
_LOGGER.debug("Hub {self.title} updated")
|
||||
|
||||
await update_devices(self.hass, self.config_entry, self.api.rollers)
|
||||
self.hass.config_entries.async_update_entry(self.config_entry, title=self.title)
|
||||
|
||||
async_dispatcher_send(
|
||||
self.hass, AUTOMATE_HUB_UPDATE.format(self.config_entry.entry_id)
|
||||
)
|
||||
|
||||
for unique_id in list(self.current_rollers):
|
||||
if unique_id not in self.api.rollers:
|
||||
_LOGGER.debug("Notifying remove of %s", unique_id)
|
||||
self.current_rollers.pop(unique_id)
|
||||
async_dispatcher_send(
|
||||
self.hass, AUTOMATE_ENTITY_REMOVE.format(unique_id)
|
||||
)
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"domain": "automate",
|
||||
"name": "Automate Pulse Hub v2",
|
||||
"config_flow": true,
|
||||
"iot_class": "local_push",
|
||||
"documentation": "https://www.home-assistant.io/integrations/automate",
|
||||
"requirements": [
|
||||
"aiopulse2==0.6.0"
|
||||
],
|
||||
"codeowners": [
|
||||
"@sillyfrog"
|
||||
]
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "El dispositiu ja est\u00e0 configurat"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Ha fallat la connexi\u00f3",
|
||||
"invalid_auth": "Autenticaci\u00f3 inv\u00e0lida",
|
||||
"unknown": "Error inesperat"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Amfitri\u00f3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Ger\u00e4t ist bereits konfiguriert"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Verbindung fehlgeschlagen",
|
||||
"invalid_auth": "Ung\u00fcltige Authentifizierung",
|
||||
"unknown": "Unerwarteter Fehler"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Host"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Failed to connect",
|
||||
"invalid_auth": "Invalid authentication",
|
||||
"unknown": "Unexpected error"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Host"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Seade on juba h\u00e4\u00e4lestatud"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00dchendamine nurjus",
|
||||
"invalid_auth": "Tuvastamine nurjus",
|
||||
"unknown": "Ootamatu t\u00f5rge"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Host"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Apparaat is al geconfigureerd"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Kan geen verbinding maken",
|
||||
"invalid_auth": "Ongeldige authenticatie",
|
||||
"unknown": "Onverwachte fout"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Host"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia",
|
||||
"invalid_auth": "Niepoprawne uwierzytelnienie",
|
||||
"unknown": "Nieoczekiwany b\u0142\u0105d"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Nazwa hosta lub adres IP"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.",
|
||||
"invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.",
|
||||
"unknown": "\u041d\u0435\u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0435\u043d\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "\u0425\u043e\u0441\u0442"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u9023\u7dda\u5931\u6557",
|
||||
"invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548",
|
||||
"unknown": "\u672a\u9810\u671f\u932f\u8aa4"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "\u4e3b\u6a5f\u7aef"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,9 +71,6 @@ from homeassistant.loader import bind_hass
|
||||
from homeassistant.util.dt import parse_datetime
|
||||
|
||||
from .config import AutomationConfig, async_validate_config_item
|
||||
|
||||
# Not used except by packages to check config structure
|
||||
from .config import PLATFORM_SCHEMA # noqa: F401
|
||||
from .const import (
|
||||
CONF_ACTION,
|
||||
CONF_INITIAL_STATE,
|
||||
|
||||
@@ -37,6 +37,8 @@ from .helpers import async_get_blueprints
|
||||
# mypy: allow-untyped-calls, allow-untyped-defs
|
||||
# mypy: no-check-untyped-defs, no-warn-return-any
|
||||
|
||||
PACKAGE_MERGE_HINT = "list"
|
||||
|
||||
_CONDITION_SCHEMA = vol.All(cv.ensure_list, [cv.CONDITION_SCHEMA])
|
||||
|
||||
PLATFORM_SCHEMA = vol.All(
|
||||
|
||||
@@ -35,19 +35,19 @@
|
||||
"on": "\u05de\u05d7\u05d5\u05d1\u05e8"
|
||||
},
|
||||
"door": {
|
||||
"off": "\u05e1\u05d2\u05d5\u05e8\u05d4",
|
||||
"off": "\u05e1\u05d2\u05d5\u05e8",
|
||||
"on": "\u05e4\u05ea\u05d5\u05d7"
|
||||
},
|
||||
"garage_door": {
|
||||
"off": "\u05e1\u05d2\u05d5\u05e8\u05d4",
|
||||
"on": "\u05e4\u05ea\u05d5\u05d7\u05d4"
|
||||
"off": "\u05e1\u05d2\u05d5\u05e8",
|
||||
"on": "\u05e4\u05ea\u05d5\u05d7"
|
||||
},
|
||||
"gas": {
|
||||
"off": "\u05e0\u05e7\u05d9",
|
||||
"on": "\u05d0\u05d5\u05ea\u05e8"
|
||||
"on": "\u05d6\u05d5\u05d4\u05d4"
|
||||
},
|
||||
"heat": {
|
||||
"off": "\u05e8\u05d2\u05d9\u05dc",
|
||||
"off": "\u05e0\u05d5\u05e8\u05de\u05dc\u05d9",
|
||||
"on": "\u05d7\u05dd"
|
||||
},
|
||||
"light": {
|
||||
@@ -64,7 +64,7 @@
|
||||
},
|
||||
"motion": {
|
||||
"off": "\u05e0\u05e7\u05d9",
|
||||
"on": "\u05d0\u05d5\u05ea\u05e8"
|
||||
"on": "\u05d6\u05d5\u05d4\u05d4"
|
||||
},
|
||||
"moving": {
|
||||
"off": "\u05dc\u05d0 \u05d6\u05d6",
|
||||
@@ -72,10 +72,10 @@
|
||||
},
|
||||
"occupancy": {
|
||||
"off": "\u05e0\u05e7\u05d9",
|
||||
"on": "\u05d0\u05d5\u05ea\u05e8"
|
||||
"on": "\u05d6\u05d5\u05d4\u05d4"
|
||||
},
|
||||
"opening": {
|
||||
"off": "\u05e0\u05e1\u05d2\u05e8",
|
||||
"off": "\u05e1\u05d2\u05d5\u05e8",
|
||||
"on": "\u05e4\u05ea\u05d5\u05d7"
|
||||
},
|
||||
"plug": {
|
||||
@@ -95,18 +95,18 @@
|
||||
},
|
||||
"smoke": {
|
||||
"off": "\u05e0\u05e7\u05d9",
|
||||
"on": "\u05d0\u05d5\u05ea\u05e8"
|
||||
"on": "\u05d6\u05d5\u05d4\u05d4"
|
||||
},
|
||||
"sound": {
|
||||
"off": "\u05e0\u05e7\u05d9",
|
||||
"on": "\u05d0\u05d5\u05ea\u05e8"
|
||||
"on": "\u05d6\u05d5\u05d4\u05d4"
|
||||
},
|
||||
"vibration": {
|
||||
"off": "\u05e0\u05e7\u05d9",
|
||||
"on": "\u05d0\u05d5\u05ea\u05e8"
|
||||
"on": "\u05d6\u05d5\u05d4\u05d4"
|
||||
},
|
||||
"window": {
|
||||
"off": "\u05e0\u05e1\u05d2\u05e8",
|
||||
"off": "\u05e1\u05d2\u05d5\u05e8",
|
||||
"on": "\u05e4\u05ea\u05d5\u05d7"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -203,29 +203,33 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||
class BluesoundPlayer(MediaPlayerEntity):
|
||||
"""Representation of a Bluesound Player."""
|
||||
|
||||
_attr_media_content_type = MEDIA_TYPE_MUSIC
|
||||
|
||||
def __init__(self, hass, host, port=DEFAULT_PORT, name=None, init_callback=None):
|
||||
def __init__(self, hass, host, port=None, name=None, init_callback=None):
|
||||
"""Initialize the media player."""
|
||||
self.host = host
|
||||
self._hass = hass
|
||||
self.port = port
|
||||
self._polling_session = async_get_clientsession(hass)
|
||||
self._polling_task = None # The actual polling task.
|
||||
self._attr_name = name
|
||||
self._name = name
|
||||
self._icon = None
|
||||
self._capture_items = []
|
||||
self._services_items = []
|
||||
self._preset_items = []
|
||||
self._sync_status = {}
|
||||
self._status = None
|
||||
self._is_online = None
|
||||
self._last_status_update = None
|
||||
self._is_online = False
|
||||
self._retry_remove = None
|
||||
self._muted = False
|
||||
self._master = None
|
||||
self._group_name = None
|
||||
self._bluesound_device_name = None
|
||||
self._is_master = False
|
||||
self._group_name = None
|
||||
self._group_list = []
|
||||
self._bluesound_device_name = None
|
||||
|
||||
self._init_callback = init_callback
|
||||
if self.port is None:
|
||||
self.port = DEFAULT_PORT
|
||||
|
||||
class _TimeoutException(Exception):
|
||||
pass
|
||||
@@ -248,12 +252,12 @@ class BluesoundPlayer(MediaPlayerEntity):
|
||||
return None
|
||||
self._sync_status = resp["SyncStatus"].copy()
|
||||
|
||||
if not self.name:
|
||||
self._attr_name = self._sync_status.get("@name", self.host)
|
||||
if not self._name:
|
||||
self._name = self._sync_status.get("@name", self.host)
|
||||
if not self._bluesound_device_name:
|
||||
self._bluesound_device_name = self._sync_status.get("@name", self.host)
|
||||
if not self.icon:
|
||||
self._attr_icon = self._sync_status.get("@icon", self.host)
|
||||
if not self._icon:
|
||||
self._icon = self._sync_status.get("@icon", self.host)
|
||||
|
||||
master = self._sync_status.get("master")
|
||||
if master is not None:
|
||||
@@ -287,14 +291,14 @@ class BluesoundPlayer(MediaPlayerEntity):
|
||||
await self.async_update_status()
|
||||
|
||||
except (asyncio.TimeoutError, ClientError, BluesoundPlayer._TimeoutException):
|
||||
_LOGGER.info("Node %s is offline, retrying later", self.name)
|
||||
_LOGGER.info("Node %s is offline, retrying later", self._name)
|
||||
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT)
|
||||
self.start_polling()
|
||||
|
||||
except CancelledError:
|
||||
_LOGGER.debug("Stopping the polling of node %s", self.name)
|
||||
_LOGGER.debug("Stopping the polling of node %s", self._name)
|
||||
except Exception:
|
||||
_LOGGER.exception("Unexpected error in %s", self.name)
|
||||
_LOGGER.exception("Unexpected error in %s", self._name)
|
||||
raise
|
||||
|
||||
def start_polling(self):
|
||||
@@ -398,7 +402,7 @@ class BluesoundPlayer(MediaPlayerEntity):
|
||||
if response.status == HTTP_OK:
|
||||
result = await response.text()
|
||||
self._is_online = True
|
||||
self._attr_media_position_updated_at = dt_util.utcnow()
|
||||
self._last_status_update = dt_util.utcnow()
|
||||
self._status = xmltodict.parse(result)["status"].copy()
|
||||
|
||||
group_name = self._status.get("groupName")
|
||||
@@ -434,58 +438,11 @@ class BluesoundPlayer(MediaPlayerEntity):
|
||||
|
||||
except (asyncio.TimeoutError, ClientError):
|
||||
self._is_online = False
|
||||
self._attr_media_position_updated_at = None
|
||||
self._last_status_update = None
|
||||
self._status = None
|
||||
self.async_write_ha_state()
|
||||
_LOGGER.info("Client connection error, marking %s as offline", self.name)
|
||||
_LOGGER.info("Client connection error, marking %s as offline", self._name)
|
||||
raise
|
||||
self.update_state_attr()
|
||||
|
||||
def update_state_attr(self):
|
||||
"""Update state attributes."""
|
||||
if self._status is None:
|
||||
self._attr_state = STATE_OFF
|
||||
self._attr_supported_features = 0
|
||||
elif self.is_grouped and not self.is_master:
|
||||
self._attr_state = STATE_GROUPED
|
||||
self._attr_supported_features = (
|
||||
SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE
|
||||
)
|
||||
else:
|
||||
status = self._status.get("state")
|
||||
self._attr_state = STATE_IDLE
|
||||
if status in ("pause", "stop"):
|
||||
self._attr_state = STATE_PAUSED
|
||||
elif status in ("stream", "play"):
|
||||
self._attr_state = STATE_PLAYING
|
||||
supported = SUPPORT_CLEAR_PLAYLIST
|
||||
if self._status.get("indexing", "0") == "0":
|
||||
supported = (
|
||||
supported
|
||||
| SUPPORT_PAUSE
|
||||
| SUPPORT_PREVIOUS_TRACK
|
||||
| SUPPORT_NEXT_TRACK
|
||||
| SUPPORT_PLAY_MEDIA
|
||||
| SUPPORT_STOP
|
||||
| SUPPORT_PLAY
|
||||
| SUPPORT_SELECT_SOURCE
|
||||
| SUPPORT_SHUFFLE_SET
|
||||
)
|
||||
if self.volume_level is not None and self.volume_level >= 0:
|
||||
supported = (
|
||||
supported
|
||||
| SUPPORT_VOLUME_STEP
|
||||
| SUPPORT_VOLUME_SET
|
||||
| SUPPORT_VOLUME_MUTE
|
||||
)
|
||||
if self._status.get("canSeek", "") == "1":
|
||||
supported = supported | SUPPORT_SEEK
|
||||
self._attr_supported_features = supported
|
||||
self._attr_extra_state_attributes = {}
|
||||
if self._group_list:
|
||||
self._attr_extra_state_attributes = {ATTR_BLUESOUND_GROUP: self._group_list}
|
||||
self._attr_extra_state_attributes[ATTR_MASTER] = self._is_master
|
||||
self._attr_shuffle = self._status.get("shuffle", "0") == "1"
|
||||
|
||||
async def async_trigger_sync_on_all(self):
|
||||
"""Trigger sync status update on all devices."""
|
||||
@@ -585,6 +542,27 @@ class BluesoundPlayer(MediaPlayerEntity):
|
||||
|
||||
return self._services_items
|
||||
|
||||
@property
|
||||
def media_content_type(self):
|
||||
"""Content type of current playing media."""
|
||||
return MEDIA_TYPE_MUSIC
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the device."""
|
||||
if self._status is None:
|
||||
return STATE_OFF
|
||||
|
||||
if self.is_grouped and not self.is_master:
|
||||
return STATE_GROUPED
|
||||
|
||||
status = self._status.get("state")
|
||||
if status in ("pause", "stop"):
|
||||
return STATE_PAUSED
|
||||
if status in ("stream", "play"):
|
||||
return STATE_PLAYING
|
||||
return STATE_IDLE
|
||||
|
||||
@property
|
||||
def media_title(self):
|
||||
"""Title of current playing media."""
|
||||
@@ -639,7 +617,7 @@ class BluesoundPlayer(MediaPlayerEntity):
|
||||
return None
|
||||
|
||||
mediastate = self.state
|
||||
if self.media_position_updated_at is None or mediastate == STATE_IDLE:
|
||||
if self._last_status_update is None or mediastate == STATE_IDLE:
|
||||
return None
|
||||
|
||||
position = self._status.get("secs")
|
||||
@@ -648,9 +626,7 @@ class BluesoundPlayer(MediaPlayerEntity):
|
||||
|
||||
position = float(position)
|
||||
if mediastate == STATE_PLAYING:
|
||||
position += (
|
||||
dt_util.utcnow() - self.media_position_updated_at
|
||||
).total_seconds()
|
||||
position += (dt_util.utcnow() - self._last_status_update).total_seconds()
|
||||
|
||||
return position
|
||||
|
||||
@@ -665,6 +641,11 @@ class BluesoundPlayer(MediaPlayerEntity):
|
||||
return None
|
||||
return float(duration)
|
||||
|
||||
@property
|
||||
def media_position_updated_at(self):
|
||||
"""Last time status was updated."""
|
||||
return self._last_status_update
|
||||
|
||||
@property
|
||||
def volume_level(self):
|
||||
"""Volume level of the media player (0..1)."""
|
||||
@@ -687,11 +668,21 @@ class BluesoundPlayer(MediaPlayerEntity):
|
||||
mute = bool(int(mute))
|
||||
return mute
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def bluesound_device_name(self):
|
||||
"""Return the device name as returned by the device."""
|
||||
return self._bluesound_device_name
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon of the device."""
|
||||
return self._icon
|
||||
|
||||
@property
|
||||
def source_list(self):
|
||||
"""List of available input sources."""
|
||||
@@ -787,15 +778,58 @@ class BluesoundPlayer(MediaPlayerEntity):
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_master(self) -> bool:
|
||||
def supported_features(self):
|
||||
"""Flag of media commands that are supported."""
|
||||
if self._status is None:
|
||||
return 0
|
||||
|
||||
if self.is_grouped and not self.is_master:
|
||||
return SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE
|
||||
|
||||
supported = SUPPORT_CLEAR_PLAYLIST
|
||||
|
||||
if self._status.get("indexing", "0") == "0":
|
||||
supported = (
|
||||
supported
|
||||
| SUPPORT_PAUSE
|
||||
| SUPPORT_PREVIOUS_TRACK
|
||||
| SUPPORT_NEXT_TRACK
|
||||
| SUPPORT_PLAY_MEDIA
|
||||
| SUPPORT_STOP
|
||||
| SUPPORT_PLAY
|
||||
| SUPPORT_SELECT_SOURCE
|
||||
| SUPPORT_SHUFFLE_SET
|
||||
)
|
||||
|
||||
current_vol = self.volume_level
|
||||
if current_vol is not None and current_vol >= 0:
|
||||
supported = (
|
||||
supported
|
||||
| SUPPORT_VOLUME_STEP
|
||||
| SUPPORT_VOLUME_SET
|
||||
| SUPPORT_VOLUME_MUTE
|
||||
)
|
||||
|
||||
if self._status.get("canSeek", "") == "1":
|
||||
supported = supported | SUPPORT_SEEK
|
||||
|
||||
return supported
|
||||
|
||||
@property
|
||||
def is_master(self):
|
||||
"""Return true if player is a coordinator."""
|
||||
return self._is_master
|
||||
|
||||
@property
|
||||
def is_grouped(self) -> bool:
|
||||
def is_grouped(self):
|
||||
"""Return true if player is a coordinator."""
|
||||
return self._master is not None or self._is_master
|
||||
|
||||
@property
|
||||
def shuffle(self):
|
||||
"""Return true if shuffle is active."""
|
||||
return self._status.get("shuffle", "0") == "1"
|
||||
|
||||
async def async_join(self, master):
|
||||
"""Join the player to a group."""
|
||||
master_device = [
|
||||
@@ -815,6 +849,17 @@ class BluesoundPlayer(MediaPlayerEntity):
|
||||
else:
|
||||
_LOGGER.error("Master not found %s", master_device)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""List members in group."""
|
||||
attributes = {}
|
||||
if self._group_list:
|
||||
attributes = {ATTR_BLUESOUND_GROUP: self._group_list}
|
||||
|
||||
attributes[ATTR_MASTER] = self._is_master
|
||||
|
||||
return attributes
|
||||
|
||||
def rebuild_bluesound_group(self):
|
||||
"""Rebuild the list of entities in speaker group."""
|
||||
if self._group_name is None:
|
||||
|
||||
@@ -335,11 +335,6 @@ class BMWConnectedDriveBaseEntity(Entity):
|
||||
"manufacturer": vehicle.attributes.get("brand"),
|
||||
}
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""Return the state attributes of the sensor."""
|
||||
return self._attrs
|
||||
|
||||
def update_callback(self):
|
||||
"""Schedule a state update."""
|
||||
self.schedule_update_ha_state(True)
|
||||
|
||||
@@ -85,54 +85,38 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, BinarySensorEntity):
|
||||
def update(self):
|
||||
"""Read new state data from the library."""
|
||||
vehicle_state = self._vehicle.state
|
||||
result = self._attrs.copy()
|
||||
|
||||
# device class opening: On means open, Off means closed
|
||||
if self._attribute == "lids":
|
||||
_LOGGER.debug("Status of lid: %s", vehicle_state.all_lids_closed)
|
||||
self._attr_state = not vehicle_state.all_lids_closed
|
||||
if self._attribute == "windows":
|
||||
self._attr_state = not vehicle_state.all_windows_closed
|
||||
# device class lock: On means unlocked, Off means locked
|
||||
if self._attribute == "door_lock_state":
|
||||
# Possible values: LOCKED, SECURED, SELECTIVE_LOCKED, UNLOCKED
|
||||
self._attr_state = vehicle_state.door_lock_state not in [
|
||||
LockState.LOCKED,
|
||||
LockState.SECURED,
|
||||
]
|
||||
# device class light: On means light detected, Off means no light
|
||||
if self._attribute == "lights_parking":
|
||||
self._attr_state = vehicle_state.are_parking_lights_on
|
||||
# device class problem: On means problem detected, Off means no problem
|
||||
if self._attribute == "condition_based_services":
|
||||
self._attr_state = not vehicle_state.are_all_cbs_ok
|
||||
if self._attribute == "check_control_messages":
|
||||
self._attr_state = vehicle_state.has_check_control_messages
|
||||
# device class power: On means power detected, Off means no power
|
||||
if self._attribute == "charging_status":
|
||||
self._attr_state = vehicle_state.charging_status in [ChargingState.CHARGING]
|
||||
# device class plug: On means device is plugged in,
|
||||
# Off means device is unplugged
|
||||
if self._attribute == "connection_status":
|
||||
self._attr_state = vehicle_state.connection_status == "CONNECTED"
|
||||
|
||||
vehicle_state = self._vehicle.state
|
||||
result = self._attrs.copy()
|
||||
|
||||
if self._attribute == "lids":
|
||||
self._attr_is_on = not vehicle_state.all_lids_closed
|
||||
for lid in vehicle_state.lids:
|
||||
result[lid.name] = lid.state.value
|
||||
elif self._attribute == "windows":
|
||||
self._attr_is_on = not vehicle_state.all_windows_closed
|
||||
for window in vehicle_state.windows:
|
||||
result[window.name] = window.state.value
|
||||
# device class lock: On means unlocked, Off means locked
|
||||
elif self._attribute == "door_lock_state":
|
||||
# Possible values: LOCKED, SECURED, SELECTIVE_LOCKED, UNLOCKED
|
||||
self._attr_is_on = vehicle_state.door_lock_state not in [
|
||||
LockState.LOCKED,
|
||||
LockState.SECURED,
|
||||
]
|
||||
result["door_lock_state"] = vehicle_state.door_lock_state.value
|
||||
result["last_update_reason"] = vehicle_state.last_update_reason
|
||||
# device class light: On means light detected, Off means no light
|
||||
elif self._attribute == "lights_parking":
|
||||
self._attr_is_on = vehicle_state.are_parking_lights_on
|
||||
result["lights_parking"] = vehicle_state.parking_lights.value
|
||||
# device class problem: On means problem detected, Off means no problem
|
||||
elif self._attribute == "condition_based_services":
|
||||
self._attr_is_on = not vehicle_state.are_all_cbs_ok
|
||||
for report in vehicle_state.condition_based_services:
|
||||
result.update(self._format_cbs_report(report))
|
||||
elif self._attribute == "check_control_messages":
|
||||
self._attr_is_on = vehicle_state.has_check_control_messages
|
||||
check_control_messages = vehicle_state.check_control_messages
|
||||
has_check_control_messages = vehicle_state.has_check_control_messages
|
||||
if has_check_control_messages:
|
||||
@@ -142,13 +126,18 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, BinarySensorEntity):
|
||||
result["check_control_messages"] = cbs_list
|
||||
else:
|
||||
result["check_control_messages"] = "OK"
|
||||
# device class power: On means power detected, Off means no power
|
||||
elif self._attribute == "charging_status":
|
||||
self._attr_is_on = vehicle_state.charging_status in [ChargingState.CHARGING]
|
||||
result["charging_status"] = vehicle_state.charging_status.value
|
||||
result["last_charging_end_result"] = vehicle_state.last_charging_end_result
|
||||
# device class plug: On means device is plugged in,
|
||||
# Off means device is unplugged
|
||||
elif self._attribute == "connection_status":
|
||||
self._attr_is_on = vehicle_state.connection_status == "CONNECTED"
|
||||
result["connection_status"] = vehicle_state.connection_status
|
||||
|
||||
self._attr_extra_state_attributes = sorted(result.items())
|
||||
self._attr_extra_state_attributes = result
|
||||
|
||||
def _format_cbs_report(self, report):
|
||||
result = {}
|
||||
|
||||
@@ -59,6 +59,7 @@ class BMWDeviceTracker(BMWConnectedDriveBaseEntity, TrackerEntity):
|
||||
|
||||
def update(self):
|
||||
"""Update state of the decvice tracker."""
|
||||
self._attr_extra_state_attributes = self._attrs
|
||||
self._location = (
|
||||
self._vehicle.state.gps_position
|
||||
if self._vehicle.state.is_vehicle_tracking_enabled
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "bmw_connected_drive",
|
||||
"name": "BMW Connected Drive",
|
||||
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
|
||||
"requirements": ["bimmer_connected==0.7.15"],
|
||||
"requirements": ["bimmer_connected==0.7.18"],
|
||||
"codeowners": ["@gerard33", "@rikroe"],
|
||||
"config_flow": true,
|
||||
"iot_class": "cloud_polling"
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"reauth_successful": "R\u00e9-authentification r\u00e9ussie"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_auth": "Authentification incorrecte",
|
||||
"pairing_failed": "L'appairage a \u00e9chou\u00e9\u00a0; veuillez v\u00e9rifier que le Bosch Smart Home Controller est en mode d'appairage (voyant clignotant) et que votre mot de passe est correct.",
|
||||
"session_error": "Erreur de session\u00a0: l'API renvoie un r\u00e9sultat non-OK.",
|
||||
"unknown": "Erreur inattendue"
|
||||
},
|
||||
"flow_title": "Bosch SHC: {name}",
|
||||
"step": {
|
||||
"confirm_discovery": {
|
||||
"description": "Veuillez appuyer sur le bouton situ\u00e9 \u00e0 l'avant du Bosch Smart Home Controller jusqu'\u00e0 ce que le voyant commence \u00e0 clignoter.\n Pr\u00eat \u00e0 continuer \u00e0 configurer {model} @ {host} avec Home Assistant\u00a0?"
|
||||
},
|
||||
"credentials": {
|
||||
"data": {
|
||||
"password": "Mot de passe du contr\u00f4leur Smart Home"
|
||||
}
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"description": "L'int\u00e9gration bosch_shc doit r\u00e9-authentifier votre compte",
|
||||
"title": "R\u00e9authentification de l'int\u00e9gration"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "H\u00f4te"
|
||||
},
|
||||
"description": "Configurez votre Bosch Smart Home Controller pour permettre la surveillance et le contr\u00f4le avec Home Assistant.",
|
||||
"title": "Param\u00e8tres d'authentification SHC"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "Bosch SHC"
|
||||
}
|
||||
@@ -69,7 +69,13 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
sensors = [
|
||||
BroadlinkSensor(device, monitored_condition)
|
||||
for monitored_condition in sensor_data
|
||||
if sensor_data[monitored_condition] != 0 or device.api.type == "A1"
|
||||
if monitored_condition in SENSOR_TYPES
|
||||
and (
|
||||
# These devices have optional sensors.
|
||||
# We don't create entities if the value is 0.
|
||||
sensor_data[monitored_condition] != 0
|
||||
or device.api.type not in {"RM4PRO", "RM4MINI"}
|
||||
)
|
||||
]
|
||||
async_add_entities(sensors)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"error": {
|
||||
"cannot_connect": "Verbindung fehlgeschlagen",
|
||||
"snmp_error": "SNMP-Server deaktiviert oder Drucker nicht unterst\u00fctzt.",
|
||||
"wrong_host": " Ung\u00fcltiger Hostname oder IP-Adresse"
|
||||
"wrong_host": "Ung\u00fcltiger Hostname oder IP-Adresse"
|
||||
},
|
||||
"flow_title": "{model} {serial_number}",
|
||||
"step": {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'emplacement est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "L'emplacement est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
"on": "\u05de\u05d5\u05e4\u05e2\u05dc"
|
||||
}
|
||||
},
|
||||
"title": "\u05dc\u05d5\u05bc\u05d7\u05b7 \u05e9\u05c1\u05b8\u05e0\u05b8\u05d4"
|
||||
"title": "\u05dc\u05d5\u05d7 \u05e9\u05e0\u05d4"
|
||||
}
|
||||
@@ -113,7 +113,6 @@ class CanarySensor(CoordinatorEntity, SensorEntity):
|
||||
canary_sensor_type = SensorType.BATTERY
|
||||
|
||||
self._canary_type = canary_sensor_type
|
||||
self._attr_state = self.reading
|
||||
self._attr_unique_id = f"{device.device_id}_{sensor_type[0]}"
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(DOMAIN, str(device.device_id))},
|
||||
@@ -144,6 +143,11 @@ class CanarySensor(CoordinatorEntity, SensorEntity):
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def state(self) -> float | None:
|
||||
"""Return the state of the sensor."""
|
||||
return self.reading
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, str] | None:
|
||||
"""Return the state attributes."""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"single_instance_allowed": "\u05e8\u05e7 \u05d4\u05d2\u05d3\u05e8\u05d4 \u05d0\u05d7\u05ea \u05e9\u05dc Google Cast \u05e0\u05d7\u05d5\u05e6\u05d4."
|
||||
"single_instance_allowed": "\u05ea\u05e6\u05d5\u05e8\u05ea\u05d5 \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4. \u05e8\u05e7 \u05ea\u05e6\u05d5\u05e8\u05d4 \u05d0\u05d7\u05ea \u05d0\u05e4\u05e9\u05e8\u05d9\u05ea."
|
||||
},
|
||||
"error": {
|
||||
"invalid_known_hosts": "\u05de\u05d0\u05e8\u05d7\u05d9\u05dd \u05d9\u05d3\u05d5\u05e2\u05d9\u05dd \u05d7\u05d9\u05d9\u05d1\u05d9\u05dd \u05dc\u05d4\u05d9\u05d5\u05ea \u05e8\u05e9\u05d9\u05de\u05ea \u05de\u05d0\u05e8\u05d7\u05d9\u05dd \u05d4\u05de\u05d5\u05e4\u05e8\u05d3\u05ea \u05d1\u05e4\u05e1\u05d9\u05e7\u05d9\u05dd."
|
||||
|
||||
@@ -66,7 +66,7 @@ DEFAULT_FORECAST_TYPE = DAILY
|
||||
DOMAIN = "climacell"
|
||||
ATTRIBUTION = "Powered by ClimaCell"
|
||||
|
||||
MAX_REQUESTS_PER_DAY = 500
|
||||
MAX_REQUESTS_PER_DAY = 100
|
||||
|
||||
CLEAR_CONDITIONS = {"night": ATTR_CONDITION_CLEAR_NIGHT, "day": ATTR_CONDITION_SUNNY}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"data": {
|
||||
"timestep": "Minuten zwischen den Kurzvorhersagen"
|
||||
},
|
||||
"description": "Wenn du die Vorhersage-Entitit\u00e4t \"Kurzvorhersage\" aktivierst, kannst du die Anzahl der Minuten zwischen den einzelnen Vorhersagen konfigurieren. Die Anzahl der bereitgestellten Vorhersagen h\u00e4ngt von der Anzahl der zwischen den Vorhersagen gew\u00e4hlten Minuten ab.",
|
||||
"description": "Wenn du die Vorhersage-Entitit\u00e4t \"Kurzvorhersage\" aktivierst, kannst du die Anzahl der Minuten zwischen den einzelnen Vorhersagen konfigurieren. Die Anzahl der bereitgestellten Vorhersagen h\u00e4ngt von der Anzahl der zwischen den Vorhersagen gew\u00e4hlten Minuten ab.",
|
||||
"title": "Aktualisiere ClimaCell-Optionen"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +143,7 @@ class CloudOAuth2Implementation(config_entry_oauth2_flow.AbstractOAuth2Implement
|
||||
|
||||
async def _async_refresh_token(self, token: dict) -> dict:
|
||||
"""Refresh a token."""
|
||||
return await account_link.async_fetch_access_token(
|
||||
new_token = await account_link.async_fetch_access_token(
|
||||
self.hass.data[DOMAIN], self.service, token["refresh_token"]
|
||||
)
|
||||
return {**token, **new_token}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "cloud",
|
||||
"name": "Home Assistant Cloud",
|
||||
"documentation": "https://www.home-assistant.io/integrations/cloud",
|
||||
"requirements": ["hass-nabucasa==0.44.0"],
|
||||
"requirements": ["hass-nabucasa==0.46.0"],
|
||||
"dependencies": ["http", "webhook"],
|
||||
"after_dependencies": ["google_assistant", "alexa"],
|
||||
"codeowners": ["@home-assistant/cloud"],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9",
|
||||
"single_instance_allowed": "Ji\u017e nastaveno. Je mo\u017en\u00e1 pouze jedin\u00e1 konfigurace.",
|
||||
"unknown": "Neo\u010dek\u00e1van\u00e1 chyba"
|
||||
},
|
||||
@@ -11,6 +12,11 @@
|
||||
},
|
||||
"flow_title": "Cloudflare: {name}",
|
||||
"step": {
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_token": "API token"
|
||||
}
|
||||
},
|
||||
"records": {
|
||||
"data": {
|
||||
"records": "Z\u00e1znamy"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"reauth_successful": "R\u00e9-authentification r\u00e9ussie",
|
||||
"single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible.",
|
||||
"unknown": "Erreur inattendue"
|
||||
},
|
||||
@@ -11,6 +12,12 @@
|
||||
},
|
||||
"flow_title": "Cloudflare: {name}",
|
||||
"step": {
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_token": "Jeton API",
|
||||
"description": "R\u00e9-authentifiez-vous avec votre compte Cloudflare."
|
||||
}
|
||||
},
|
||||
"records": {
|
||||
"data": {
|
||||
"records": "Enregistrements"
|
||||
|
||||
@@ -118,7 +118,8 @@ class CO2Sensor(update_coordinator.CoordinatorEntity[CO2SignalResponse], SensorE
|
||||
def available(self) -> bool:
|
||||
"""Return True if entity is available."""
|
||||
return (
|
||||
super().available and self._description.key in self.coordinator.data["data"]
|
||||
super().available
|
||||
and self.coordinator.data["data"].get(self._description.key) is not None
|
||||
)
|
||||
|
||||
@property
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno",
|
||||
"unknown": "Neo\u010dek\u00e1van\u00e1 chyba"
|
||||
},
|
||||
"error": {
|
||||
"invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed",
|
||||
"unknown": "Neo\u010dek\u00e1van\u00e1 chyba"
|
||||
},
|
||||
"step": {
|
||||
"coordinates": {
|
||||
"data": {
|
||||
"latitude": "Zem\u011bpisn\u00e1 \u0161\u00ed\u0159ka",
|
||||
"longitude": "Zem\u011bpisn\u00e1 d\u00e9lka"
|
||||
}
|
||||
},
|
||||
"country": {
|
||||
"data": {
|
||||
"country_code": "K\u00f3d zem\u011b"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "P\u0159\u00edstupov\u00fd token"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"api_ratelimit": "Limite de d\u00e9bit de l\u2019API d\u00e9pass\u00e9e",
|
||||
"unknown": "Erreur inattendue"
|
||||
},
|
||||
"error": {
|
||||
@@ -22,8 +24,10 @@
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "Token d'acc\u00e8s"
|
||||
}
|
||||
"api_key": "Token d'acc\u00e8s",
|
||||
"location": "Obtenir des donn\u00e9es pour"
|
||||
},
|
||||
"description": "Visitez https://co2signal.com/ pour demander un jeton."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato",
|
||||
"api_ratelimit": "Limite di frequenza API superato",
|
||||
"unknown": "Errore imprevisto"
|
||||
},
|
||||
"error": {
|
||||
"api_ratelimit": "Limite di frequenza API superato",
|
||||
"invalid_auth": "Autenticazione non valida",
|
||||
"unknown": "Errore imprevisto"
|
||||
},
|
||||
"step": {
|
||||
"coordinates": {
|
||||
"data": {
|
||||
"latitude": "Latitudine",
|
||||
"longitude": "Logitudine"
|
||||
}
|
||||
},
|
||||
"country": {
|
||||
"data": {
|
||||
"country_code": "Prefisso internazionale"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "Token di accesso",
|
||||
"location": "Ottieni dati per"
|
||||
},
|
||||
"description": "Visita https://co2signal.com/ per richiedere un token."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Za\u0159\u00edzen\u00ed je ji\u017e nastaveno"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit",
|
||||
"invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed",
|
||||
"unknown": "Neo\u010dek\u00e1van\u00e1 chyba"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "Kl\u00ed\u010d API"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
"currencies": "Kontostand W\u00e4hrungen",
|
||||
"exchange_rates": "Wechselkurse"
|
||||
},
|
||||
"description": "Bitte gib die Details deines API-Schl\u00fcssels ein, wie von Coinbase bereitgestellt. Trenne mehrere W\u00e4hrungen mit einem Komma (z. B. \"BTC, EUR\")",
|
||||
"description": "Bitte gib die Details deines API-Schl\u00fcssels ein, wie von Coinbase bereitgestellt.",
|
||||
"title": "Coinbase API Schl\u00fcssel Details"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_auth": "Authentification incorrecte",
|
||||
"unknown": "Erreur inattendue"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "cl\u00e9 API",
|
||||
"api_token": "API secr\u00e8te",
|
||||
"currencies": "Devises du solde du compte",
|
||||
"exchange_rates": "Taux d'\u00e9change"
|
||||
},
|
||||
"description": "Veuillez saisir les d\u00e9tails de votre cl\u00e9 API tels que fournis par Coinbase.",
|
||||
"title": "D\u00e9tails de la cl\u00e9 de l'API Coinbase"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"currency_unavaliable": "Un ou plusieurs des soldes de devises demand\u00e9s ne sont pas fournis par votre API Coinbase.",
|
||||
"exchange_rate_unavaliable": "Un ou plusieurs des taux de change demand\u00e9s ne sont pas fournis par Coinbase.",
|
||||
"unknown": "Erreur inattendue"
|
||||
},
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"account_balance_currencies": "Soldes du portefeuille \u00e0 d\u00e9clarer.",
|
||||
"exchange_base": "Devise de base pour les capteurs de taux de change.",
|
||||
"exchange_rate_currencies": "Taux de change \u00e0 d\u00e9clarer."
|
||||
},
|
||||
"description": "Ajuster les options de Coinbase"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
"currencies": "Valuta's van rekeningsaldo",
|
||||
"exchange_rates": "Wisselkoersen"
|
||||
},
|
||||
"description": "Voer de gegevens van uw API-sleutel in zoals verstrekt door Coinbase. Scheidt meerdere valuta's met een komma (bijv. \"BTC, EUR\")",
|
||||
"description": "Voer de gegevens van uw API-sleutel in zoals verstrekt door Coinbase.",
|
||||
"title": "Coinbase API Sleutel Details"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"state": {
|
||||
"_": {
|
||||
"configure": "\u05d4\u05d2\u05d3\u05e8",
|
||||
"configured": "\u05d4\u05d5\u05d2\u05d3\u05e8"
|
||||
"configure": "\u05d4\u05d2\u05d3\u05e8\u05d4",
|
||||
"configured": "\u05de\u05d5\u05d2\u05d3\u05e8"
|
||||
}
|
||||
},
|
||||
"title": "\u05e7\u05d5\u05e0\u05e4\u05d9\u05d2\u05d5\u05e8\u05d8\u05d5\u05e8"
|
||||
"title": "\u05e7\u05d5\u05d1\u05e2 \u05ea\u05e6\u05d5\u05e8\u05d4"
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"title": "\u05e9\u05c2\u05b4\u05d9\u05d7\u05b8\u05d4"
|
||||
"title": "\u05e9\u05d9\u05d7\u05d4"
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Ce pays est d\u00e9j\u00e0 configur\u00e9."
|
||||
"already_configured": "Ce pays est d\u00e9j\u00e0 configur\u00e9.",
|
||||
"cannot_connect": "\u00c9chec de connexion"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
},
|
||||
"state": {
|
||||
"_": {
|
||||
"closed": "\u05e0\u05e1\u05d2\u05e8",
|
||||
"closed": "\u05e1\u05d2\u05d5\u05e8",
|
||||
"closing": "\u05e1\u05d5\u05d2\u05e8",
|
||||
"open": "\u05e4\u05ea\u05d5\u05d7",
|
||||
"opening": "\u05e4\u05d5\u05ea\u05d7",
|
||||
"stopped": "\u05e2\u05e6\u05d5\u05e8"
|
||||
"stopped": "\u05e2\u05e6\u05e8"
|
||||
}
|
||||
},
|
||||
"title": "\u05d5\u05d9\u05dc\u05d5\u05df"
|
||||
|
||||
@@ -23,6 +23,7 @@ from homeassistant.const import (
|
||||
ATTR_TEMPERATURE,
|
||||
ATTR_VOLTAGE,
|
||||
DEVICE_CLASS_BATTERY,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
DEVICE_CLASS_POWER,
|
||||
@@ -40,6 +41,7 @@ from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
async_dispatcher_send,
|
||||
)
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import ATTR_DARK, ATTR_ON, NEW_SENSOR
|
||||
from .deconz_device import DeconzDevice
|
||||
@@ -51,6 +53,7 @@ ATTR_DAYLIGHT = "daylight"
|
||||
ATTR_EVENT_ID = "event_id"
|
||||
|
||||
DEVICE_CLASS = {
|
||||
Consumption: DEVICE_CLASS_ENERGY,
|
||||
Humidity: DEVICE_CLASS_HUMIDITY,
|
||||
LightLevel: DEVICE_CLASS_ILLUMINANCE,
|
||||
Power: DEVICE_CLASS_POWER,
|
||||
@@ -65,6 +68,7 @@ ICON = {
|
||||
}
|
||||
|
||||
STATE_CLASS = {
|
||||
Consumption: STATE_CLASS_MEASUREMENT,
|
||||
Humidity: STATE_CLASS_MEASUREMENT,
|
||||
Pressure: STATE_CLASS_MEASUREMENT,
|
||||
Temperature: STATE_CLASS_MEASUREMENT,
|
||||
@@ -158,6 +162,9 @@ class DeconzSensor(DeconzDevice, SensorEntity):
|
||||
self._attr_state_class = STATE_CLASS.get(type(self._device))
|
||||
self._attr_unit_of_measurement = UNIT_OF_MEASUREMENT.get(type(self._device))
|
||||
|
||||
if device.type in Consumption.ZHATYPE:
|
||||
self._attr_last_reset = dt_util.utc_from_timestamp(0)
|
||||
|
||||
@callback
|
||||
def async_update_callback(self, force_update=False):
|
||||
"""Update the sensor's state."""
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"state": {
|
||||
"demo__speed": {
|
||||
"light_speed": "Vitesse de la lumi\u00e8re",
|
||||
"ludicrous_speed": "Vitesse ridicule",
|
||||
"ridiculous_speed": "Vitesse ridicule"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,5 +5,5 @@
|
||||
"not_home": "\u05dc\u05d0 \u05d1\u05d1\u05d9\u05ea"
|
||||
}
|
||||
},
|
||||
"title": "\u05de\u05e2\u05e7\u05d1 \u05de\u05db\u05e9\u05d9\u05e8"
|
||||
"title": "\u05de\u05e2\u05e7\u05d1 \u05d0\u05d7\u05e8 \u05d4\u05ea\u05e7\u05e0\u05d9\u05dd"
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u00da\u010det je ji\u017e nastaven"
|
||||
"already_configured": "\u00da\u010det je ji\u017e nastaven",
|
||||
"reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed"
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Cette unit\u00e9 centrale Home Control est d\u00e9j\u00e0 utilis\u00e9e."
|
||||
"already_configured": "Cette unit\u00e9 centrale Home Control est d\u00e9j\u00e0 utilis\u00e9e.",
|
||||
"reauth_successful": "R\u00e9-authentification r\u00e9ussie"
|
||||
},
|
||||
"error": {
|
||||
"invalid_auth": "Authentification invalide"
|
||||
"invalid_auth": "Authentification invalide",
|
||||
"reauth_failed": "Veuillez utiliser le m\u00eame utilisateur mydevolo que pr\u00e9c\u00e9demment."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "\u05e1\u05d9\u05e1\u05de\u05d4",
|
||||
"username": "\u05d3\u05d5\u05d0\u05e8 \u05d0\u05dc\u05e7\u05d8\u05e8\u05d5\u05e0\u05d9/ \u05de\u05d6\u05d4\u05d4 devolo"
|
||||
"username": "\u05d3\u05d5\u05d0\"\u05dc / \u05de\u05d6\u05d4\u05d4 devolo"
|
||||
}
|
||||
},
|
||||
"zeroconf_confirm": {
|
||||
|
||||
@@ -9,6 +9,14 @@
|
||||
},
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"ssdp_confirm": {
|
||||
"data": {
|
||||
"many": "\u05e8\u05d9\u05e7\u05d9\u05dd",
|
||||
"one": "\u05e8\u05d9\u05e7",
|
||||
"other": "",
|
||||
"two": "\u05e8\u05d9\u05e7\u05d9\u05dd"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "\u05de\u05d0\u05e8\u05d7"
|
||||
|
||||
@@ -157,7 +157,9 @@ async def async_setup_entry(
|
||||
update_entities_telegram({})
|
||||
|
||||
# throttle reconnect attempts
|
||||
await asyncio.sleep(entry.data[CONF_RECONNECT_INTERVAL])
|
||||
await asyncio.sleep(
|
||||
entry.data.get(CONF_RECONNECT_INTERVAL, DEFAULT_RECONNECT_INTERVAL)
|
||||
)
|
||||
|
||||
except (serial.serialutil.SerialException, OSError):
|
||||
# Log any error while establishing connection and drop to retry
|
||||
@@ -165,6 +167,11 @@ async def async_setup_entry(
|
||||
LOGGER.exception("Error connecting to DSMR")
|
||||
transport = None
|
||||
protocol = None
|
||||
|
||||
# throttle reconnect attempts
|
||||
await asyncio.sleep(
|
||||
entry.data.get(CONF_RECONNECT_INTERVAL, DEFAULT_RECONNECT_INTERVAL)
|
||||
)
|
||||
except CancelledError:
|
||||
if stop_listener:
|
||||
stop_listener() # pylint: disable=not-callable
|
||||
|
||||
@@ -1,23 +1,46 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"cannot_communicate": "\u00c9chec de la communication",
|
||||
"cannot_connect": "\u00c9chec de connexion"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
|
||||
"cannot_communicate": "\u00c9chec de la communication",
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"one": "Vide",
|
||||
"other": "Vide"
|
||||
},
|
||||
"step": {
|
||||
"one": "",
|
||||
"other": "Autre",
|
||||
"setup_network": {
|
||||
"data": {
|
||||
"dsmr_version": "S\u00e9lectionner la version DSMR",
|
||||
"host": "H\u00f4te",
|
||||
"port": "Port"
|
||||
},
|
||||
"title": "S\u00e9lectionner l'adresse de connexion"
|
||||
},
|
||||
"setup_serial": {
|
||||
"data": {
|
||||
"dsmr_version": "S\u00e9lectionner la version DSMR",
|
||||
"port": "S\u00e9lectionner un appareil"
|
||||
},
|
||||
"title": "Appareil"
|
||||
},
|
||||
"setup_serial_manual_path": {
|
||||
"data": {
|
||||
"port": "Chemin du p\u00e9riph\u00e9rique USB"
|
||||
},
|
||||
"title": "Chemin"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"type": "Type de connexion"
|
||||
},
|
||||
"title": "S\u00e9lectionner le type de connexion"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,39 +4,27 @@ from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.util import slugify
|
||||
|
||||
from .definitions import DEFINITIONS
|
||||
from .definitions import SENSORS, DSMRReaderSensorEntityDescription
|
||||
|
||||
DOMAIN = "dsmr_reader"
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up DSMR Reader sensors."""
|
||||
|
||||
sensors = []
|
||||
for topic in DEFINITIONS:
|
||||
sensors.append(DSMRSensor(topic))
|
||||
|
||||
async_add_entities(sensors)
|
||||
async_add_entities(DSMRSensor(description) for description in SENSORS)
|
||||
|
||||
|
||||
class DSMRSensor(SensorEntity):
|
||||
"""Representation of a DSMR sensor that is updated via MQTT."""
|
||||
|
||||
def __init__(self, topic):
|
||||
entity_description: DSMRReaderSensorEntityDescription
|
||||
|
||||
def __init__(self, description: DSMRReaderSensorEntityDescription) -> None:
|
||||
"""Initialize the sensor."""
|
||||
self.entity_description = description
|
||||
|
||||
self._definition = DEFINITIONS[topic]
|
||||
|
||||
self._entity_id = slugify(topic.replace("/", "_"))
|
||||
self._topic = topic
|
||||
|
||||
self._name = self._definition.get("name", topic.split("/")[-1])
|
||||
self._device_class = self._definition.get("device_class")
|
||||
self._enable_default = self._definition.get("enable_default")
|
||||
self._unit_of_measurement = self._definition.get("unit")
|
||||
self._icon = self._definition.get("icon")
|
||||
self._transform = self._definition.get("transform")
|
||||
self._state = None
|
||||
slug = slugify(description.key.replace("/", "_"))
|
||||
self.entity_id = f"sensor.{slug}"
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Subscribe to MQTT events."""
|
||||
@@ -44,47 +32,13 @@ class DSMRSensor(SensorEntity):
|
||||
@callback
|
||||
def message_received(message):
|
||||
"""Handle new MQTT messages."""
|
||||
|
||||
if self._transform is not None:
|
||||
self._state = self._transform(message.payload)
|
||||
if self.entity_description.state is not None:
|
||||
self._attr_state = self.entity_description.state(message.payload)
|
||||
else:
|
||||
self._state = message.payload
|
||||
self._attr_state = message.payload
|
||||
|
||||
self.async_write_ha_state()
|
||||
|
||||
await mqtt.async_subscribe(self.hass, self._topic, message_received, 1)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor supplied in constructor."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def entity_id(self):
|
||||
"""Return the entity ID for this sensor."""
|
||||
return f"sensor.{self._entity_id}"
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the current state of the entity."""
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the device_class of this sensor."""
|
||||
return self._device_class
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit_of_measurement of this sensor."""
|
||||
return self._unit_of_measurement
|
||||
|
||||
@property
|
||||
def entity_registry_enabled_default(self) -> bool:
|
||||
"""Return if the entity should be enabled when first added to the entity registry."""
|
||||
return self._enable_default
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon of this sensor."""
|
||||
return self._icon
|
||||
await mqtt.async_subscribe(
|
||||
self.hass, self.entity_description.key, message_received, 1
|
||||
)
|
||||
|
||||
@@ -122,10 +122,12 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
),
|
||||
)
|
||||
|
||||
SENSOR_TYPE_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_MONITORED_VARIABLES): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSOR_TYPES)]
|
||||
cv.ensure_list, [vol.In(SENSOR_TYPE_KEYS)]
|
||||
),
|
||||
vol.Required(CONF_USERNAME): cv.string,
|
||||
vol.Required(CONF_PASSWORD): cv.string,
|
||||
|
||||
@@ -223,7 +223,6 @@ SENSOR_TYPES = {
|
||||
None,
|
||||
4,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
None,
|
||||
],
|
||||
"Flame": ["Flame", None, "mdi:toggle-switch", 2, None],
|
||||
"PowerEnergyConsumptionHeatingCircuit": [
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"confirm": {
|
||||
"description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea {model} ({host})?"
|
||||
"description": "\u05d4\u05d0\u05dd \u05d1\u05e8\u05e6\u05d5\u05e0\u05da \u05dc\u05d4\u05d2\u05d3\u05d9\u05e8 \u05d0\u05ea {name} ({host})?"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user