mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Merge remote-tracking branch 'upstream/dev' into igd
This commit is contained in:
commit
553d3c0179
@ -52,6 +52,8 @@ homeassistant/components/cover/template.py @PhracturedBlue
|
|||||||
homeassistant/components/device_tracker/automatic.py @armills
|
homeassistant/components/device_tracker/automatic.py @armills
|
||||||
homeassistant/components/device_tracker/tile.py @bachya
|
homeassistant/components/device_tracker/tile.py @bachya
|
||||||
homeassistant/components/history_graph.py @andrey-git
|
homeassistant/components/history_graph.py @andrey-git
|
||||||
|
homeassistant/components/light/lifx.py @amelchio
|
||||||
|
homeassistant/components/light/lifx_legacy.py @amelchio
|
||||||
homeassistant/components/light/tplink.py @rytilahti
|
homeassistant/components/light/tplink.py @rytilahti
|
||||||
homeassistant/components/light/yeelight.py @rytilahti
|
homeassistant/components/light/yeelight.py @rytilahti
|
||||||
homeassistant/components/lock/nello.py @pschmitt
|
homeassistant/components/lock/nello.py @pschmitt
|
||||||
@ -65,6 +67,7 @@ homeassistant/components/media_player/sonos.py @amelchio
|
|||||||
homeassistant/components/media_player/xiaomi_tv.py @fattdev
|
homeassistant/components/media_player/xiaomi_tv.py @fattdev
|
||||||
homeassistant/components/media_player/yamaha_musiccast.py @jalmeroth
|
homeassistant/components/media_player/yamaha_musiccast.py @jalmeroth
|
||||||
homeassistant/components/plant.py @ChristianKuehnel
|
homeassistant/components/plant.py @ChristianKuehnel
|
||||||
|
homeassistant/components/scene/lifx_cloud.py @amelchio
|
||||||
homeassistant/components/sensor/airvisual.py @bachya
|
homeassistant/components/sensor/airvisual.py @bachya
|
||||||
homeassistant/components/sensor/filter.py @dgomes
|
homeassistant/components/sensor/filter.py @dgomes
|
||||||
homeassistant/components/sensor/gearbest.py @HerrHofrat
|
homeassistant/components/sensor/gearbest.py @HerrHofrat
|
||||||
|
@ -15,7 +15,7 @@ import homeassistant.helpers.config_validation as cv
|
|||||||
from homeassistant.helpers.dispatcher import (
|
from homeassistant.helpers.dispatcher import (
|
||||||
async_dispatcher_connect, async_dispatcher_send)
|
async_dispatcher_connect, async_dispatcher_send)
|
||||||
|
|
||||||
REQUIREMENTS = ['asterisk_mbox==0.4.0']
|
REQUIREMENTS = ['asterisk_mbox==0.5.0']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
7
homeassistant/components/auth/.translations/ar.json
Normal file
7
homeassistant/components/auth/.translations/ar.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"mfa_setup": {
|
||||||
|
"totp": {
|
||||||
|
"title": "TOTP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
homeassistant/components/auth/.translations/de.json
Normal file
16
homeassistant/components/auth/.translations/de.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"mfa_setup": {
|
||||||
|
"totp": {
|
||||||
|
"error": {
|
||||||
|
"invalid_code": "Ung\u00fcltiger Code, bitte versuche es erneut. Wenn Sie diesen Fehler regelm\u00e4\u00dfig erhalten, stelle sicher, dass die Uhr deines Home Assistant-Systems korrekt ist."
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"description": "Um die Zwei-Faktor-Authentifizierung mit zeitbasierten Einmalpassw\u00f6rtern zu aktivieren, scanne den QR-Code mit Ihrer Authentifizierungs-App. Wenn du keine hast, empfehlen wir entweder [Google Authenticator] (https://support.google.com/accounts/answer/1066447) oder [Authy] (https://authy.com/). \n\n {qr_code} \n \nNachdem du den Code gescannt hast, gebe den sechsstelligen Code aus der App ein, um das Setup zu \u00fcberpr\u00fcfen. Wenn es Probleme beim Scannen des QR-Codes gibt, f\u00fchre ein manuelles Setup mit dem Code ** ` {code} ` ** durch.",
|
||||||
|
"title": "Richte die Zwei-Faktor-Authentifizierung mit TOTP ein"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "TOTP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
homeassistant/components/auth/.translations/es-419.json
Normal file
12
homeassistant/components/auth/.translations/es-419.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"mfa_setup": {
|
||||||
|
"totp": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"title": "Configurar la autenticaci\u00f3n de dos factores mediante TOTP"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "TOTP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
homeassistant/components/auth/.translations/fr.json
Normal file
16
homeassistant/components/auth/.translations/fr.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"mfa_setup": {
|
||||||
|
"totp": {
|
||||||
|
"error": {
|
||||||
|
"invalid_code": "Code invalide. S'il vous pla\u00eet essayez \u00e0 nouveau. Si cette erreur persiste, assurez-vous que l'horloge de votre syst\u00e8me Home Assistant est correcte."
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"description": "Pour activer l'authentification \u00e0 deux facteurs \u00e0 l'aide de mots de passe \u00e0 utilisation unique bas\u00e9s sur l'heure, num\u00e9risez le code QR avec votre application d'authentification. Si vous n'en avez pas, nous vous recommandons d'utiliser [Google Authenticator] (https://support.google.com/accounts/answer/1066447) ou [Authy] (https://authy.com/). \n\n {qr_code} \n \n Apr\u00e8s avoir num\u00e9ris\u00e9 le code, entrez le code \u00e0 six chiffres de votre application pour v\u00e9rifier la configuration. Si vous rencontrez des probl\u00e8mes lors de l\u2019analyse du code QR, effectuez une configuration manuelle avec le code ** ` {code} ` **.",
|
||||||
|
"title": "Configurer une authentification \u00e0 deux facteurs \u00e0 l'aide de TOTP"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "TOTP (Mot de passe \u00e0 utilisation unique bas\u00e9 sur le temps)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
homeassistant/components/auth/.translations/it.json
Normal file
13
homeassistant/components/auth/.translations/it.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"mfa_setup": {
|
||||||
|
"totp": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"description": "Per attivare l'autenticazione a due fattori utilizzando password monouso basate sul tempo, eseguire la scansione del codice QR con l'app di autenticazione. Se non ne hai uno, ti consigliamo [Google Authenticator] (https://support.google.com/accounts/answer/1066447) o [Authy] (https://authy.com/). \n\n {qr_code} \n \n Dopo aver scansionato il codice, inserisci il codice a sei cifre dalla tua app per verificare la configurazione. Se riscontri problemi con la scansione del codice QR, esegui una configurazione manuale con codice ** ` {code} ` **.",
|
||||||
|
"title": "Imposta l'autenticazione a due fattori usando TOTP"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "TOTP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,11 +2,11 @@
|
|||||||
"mfa_setup": {
|
"mfa_setup": {
|
||||||
"totp": {
|
"totp": {
|
||||||
"error": {
|
"error": {
|
||||||
"invalid_code": "\uc798\ubabb\ub41c \ucf54\ub4dc \uc785\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694. \uc774 \uc624\ub958\uac00 \uc9c0\uc18d\uc801\uc73c\ub85c \ubc1c\uc0dd\ud55c\ub2e4\uba74 Home Assistant \uc758 \uc2dc\uacc4\uac00 \uc815\ud655\ud55c\uc9c0 \ud655\uc778\ud574\ubcf4\uc138\uc694."
|
"invalid_code": "\uc798\ubabb\ub41c \ucf54\ub4dc \uc785\ub2c8\ub2e4. \ub2e4\uc2dc \uc2dc\ub3c4\ud574\uc8fc\uc138\uc694. \uc774 \uc624\ub958\uac00 \uc9c0\uc18d\uc801\uc73c\ub85c \ubc1c\uc0dd\ud55c\ub2e4\uba74 Home Assistant \uc758 \uc2dc\uac04\uc124\uc815\uc774 \uc62c\ubc14\ub978\uc9c0 \ud655\uc778\ud574\ubcf4\uc138\uc694."
|
||||||
},
|
},
|
||||||
"step": {
|
"step": {
|
||||||
"init": {
|
"init": {
|
||||||
"description": "\uc2dc\uac04 \uae30\ubc18\uc758 \uc77c\ud68c\uc6a9 \ube44\ubc00\ubc88\ud638\ub97c \uc0ac\uc6a9\ud558\ub294 2\ub2e8\uacc4 \uc778\uc99d\uc744 \ud558\ub824\uba74 \uc778\uc99d\uc6a9 \uc571\uc744 \uc774\uc6a9\ud574\uc11c QR \ucf54\ub4dc\ub97c \uc2a4\uce94\ud574 \uc8fc\uc138\uc694. \uc778\uc99d\uc6a9 \uc571\uc740 [Google Authenticator](https://support.google.com/accounts/answer/1066447) \ub098 [Authy](https://authy.com/) \ub97c \ucd94\ucc9c\ub4dc\ub9bd\ub2c8\ub2e4.\n\n{qr_code}\n\n\uc2a4\uce94 \ud6c4\uc5d0 \uc0dd\uc131\ub41c 6\uc790\ub9ac \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc11c \uc124\uc815\uc744 \ud655\uc778\ud558\uc138\uc694. QR \ucf54\ub4dc \uc2a4\uce94\uc5d0 \ubb38\uc81c\uac00 \uc788\ub2e4\uba74, **`{code}`** \ucf54\ub4dc\ub85c \uc9c1\uc811 \uc124\uc815\ud574\ubcf4\uc138\uc694.",
|
"description": "\uc2dc\uac04 \uae30\ubc18\uc758 \uc77c\ud68c\uc6a9 \ube44\ubc00\ubc88\ud638\ub97c \uc0ac\uc6a9\ud558\ub294 2\ub2e8\uacc4 \uc778\uc99d\uc744 \ud558\ub824\uba74 \uc778\uc99d\uc6a9 \uc571\uc744 \uc774\uc6a9\ud574\uc11c QR \ucf54\ub4dc\ub97c \uc2a4\uce94\ud574 \uc8fc\uc138\uc694. \uc778\uc99d\uc6a9 \uc571\uc740 [Google Authenticator](https://support.google.com/accounts/answer/1066447) \ub610\ub294 [Authy](https://authy.com/) \ub97c \ucd94\ucc9c\ub4dc\ub9bd\ub2c8\ub2e4.\n\n{qr_code}\n\n\uc2a4\uce94 \ud6c4\uc5d0 \uc0dd\uc131\ub41c 6\uc790\ub9ac \ucf54\ub4dc\ub97c \uc785\ub825\ud574\uc11c \uc124\uc815\uc744 \ud655\uc778\ud558\uc138\uc694. QR \ucf54\ub4dc \uc2a4\uce94\uc5d0 \ubb38\uc81c\uac00 \uc788\ub2e4\uba74, **`{code}`** \ucf54\ub4dc\ub85c \uc9c1\uc811 \uc124\uc815\ud574\ubcf4\uc138\uc694.",
|
||||||
"title": "TOTP \ub97c \uc0ac\uc6a9\ud558\uc5ec 2 \ub2e8\uacc4 \uc778\uc99d \uad6c\uc131"
|
"title": "TOTP \ub97c \uc0ac\uc6a9\ud558\uc5ec 2 \ub2e8\uacc4 \uc778\uc99d \uad6c\uc131"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
16
homeassistant/components/auth/.translations/nl.json
Normal file
16
homeassistant/components/auth/.translations/nl.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"mfa_setup": {
|
||||||
|
"totp": {
|
||||||
|
"error": {
|
||||||
|
"invalid_code": "Ongeldige code, probeer het opnieuw. Als u deze fout blijft krijgen, controleer dan of de klok van uw Home Assistant systeem correct is ingesteld."
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"description": "Voor het activeren van twee-factor-authenticatie via tijdgebonden eenmalige wachtwoorden: scan de QR code met uw authenticatie-app. Als u nog geen app heeft, adviseren we [Google Authenticator (https://support.google.com/accounts/answer/1066447) of [Authy](https://authy.com/).\n\n{qr_code}\n\nNa het scannen van de code voert u de zescijferige code uit uw app in om de instelling te controleren. Als u problemen heeft met het scannen van de QR-code, voert u een handmatige configuratie uit met code **`{code}`**.",
|
||||||
|
"title": "Configureer twee-factor-authenticatie via TOTP"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "TOTP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
homeassistant/components/auth/.translations/no.json
Normal file
16
homeassistant/components/auth/.translations/no.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"mfa_setup": {
|
||||||
|
"totp": {
|
||||||
|
"error": {
|
||||||
|
"invalid_code": "Ugyldig kode, pr\u00f8v igjen. Hvis du f\u00e5r denne feilen konsekvent, m\u00e5 du s\u00f8rge for at klokken p\u00e5 Home Assistant systemet er riktig."
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"description": "For \u00e5 aktivere tofaktorautentisering ved hjelp av tidsbaserte engangspassord, skann QR-koden med autentiseringsappen din. Hvis du ikke har en, kan vi anbefale enten [Google Authenticator](https://support.google.com/accounts/answer/1066447) eller [Authy](https://authy.com/). \n\n {qr_code} \n \nEtter at du har skannet koden, skriver du inn den seks-sifrede koden fra appen din for \u00e5 kontrollere oppsettet. Dersom du har problemer med \u00e5 skanne QR-koden kan du taste inn f\u00f8lgende kode manuelt: **`{code}`**.",
|
||||||
|
"title": "Konfigurer tofaktorautentisering ved hjelp av TOTP"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "TOTP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
homeassistant/components/auth/.translations/pl.json
Normal file
16
homeassistant/components/auth/.translations/pl.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"mfa_setup": {
|
||||||
|
"totp": {
|
||||||
|
"error": {
|
||||||
|
"invalid_code": "Nieprawid\u0142owy kod, spr\u00f3buj ponownie. Je\u015bli b\u0142\u0105d b\u0119dzie si\u0119 powtarza\u0142, upewnij si\u0119, \u017ce czas zegara systemu Home Assistant jest prawid\u0142owy."
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"description": "Aby aktywowa\u0107 uwierzytelnianie dwusk\u0142adnikowe przy u\u017cyciu jednorazowych hase\u0142 opartych na czasie, zeskanuj kod QR za pomoc\u0105 aplikacji uwierzytelniaj\u0105cej. Je\u015bli jej nie masz, polecamy [Google Authenticator](https://support.google.com/accounts/answer/1066447) lub [Authy](https://authy.com/).\n\n{qr_code} \n \nPo zeskanowaniu kodu wprowad\u017a sze\u015bciocyfrowy kod z aplikacji, aby zweryfikowa\u0107 konfiguracj\u0119. Je\u015bli masz problemy z zeskanowaniem kodu QR, wykonaj r\u0119czn\u0105 konfiguracj\u0119 z kodem **`{code}`**.",
|
||||||
|
"title": "Skonfiguruj uwierzytelnianie dwusk\u0142adnikowe za pomoc\u0105 hase\u0142 jednorazowych opartych na czasie"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "Has\u0142a jednorazowe oparte na czasie"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
homeassistant/components/auth/.translations/pt.json
Normal file
16
homeassistant/components/auth/.translations/pt.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"mfa_setup": {
|
||||||
|
"totp": {
|
||||||
|
"error": {
|
||||||
|
"invalid_code": "C\u00f3digo inv\u00e1lido, por favor, tente novamente. Se receber este erro constantemente, por favor, certifique-se de que o rel\u00f3gio do sistema que hospeda o Home Assistent \u00e9 preciso."
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"description": "Para ativar a autentica\u00e7\u00e3o com dois fatores utilizando passwords unicas temporais (OTP), ler o c\u00f3digo QR com a sua aplica\u00e7\u00e3o de autentica\u00e7\u00e3o. Se voc\u00ea n\u00e3o tiver uma, recomendamos [Google Authenticator](https://support.google.com/accounts/answer/1066447) ou [Authy](https://authy.com/).\n\n{qr_code}\n\nDepois de ler o c\u00f3digo, introduza o c\u00f3digo de seis d\u00edgitos fornecido pela sua aplica\u00e7\u00e3o para verificar a configura\u00e7\u00e3o. Se tiver problemas a ler o c\u00f3digo QR, fa\u00e7a uma configura\u00e7\u00e3o manual com o c\u00f3digo **`{c\u00f3digo}`**.",
|
||||||
|
"title": "Configurar autentica\u00e7\u00e3o com dois fatores usando TOTP"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "TOTP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@
|
|||||||
},
|
},
|
||||||
"step": {
|
"step": {
|
||||||
"init": {
|
"init": {
|
||||||
"description": "\u0427\u0442\u043e\u0431\u044b \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0443\u044e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0445 \u043f\u0430\u0440\u043e\u043b\u0435\u0439, \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043e\u0442\u0441\u043a\u0430\u043d\u0438\u0440\u0443\u0439\u0442\u0435 QR-\u043a\u043e\u0434 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0433\u043e \u043d\u0435\u0442, \u043c\u044b \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u0438\u0431\u043e [Google Authenticator] (https://support.google.com/accounts/answer/1066447), \u043b\u0438\u0431\u043e [Authy] (https://authy.com/). \n\n {qr_code} \n \n \u041f\u043e\u0441\u043b\u0435 \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f QR-\u043a\u043e\u0434\u0430 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0448\u0435\u0441\u0442\u0438\u0437\u043d\u0430\u0447\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438\u0437 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441\u043e \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c QR-\u043a\u043e\u0434\u0430, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u0440\u0443\u0447\u043d\u0443\u044e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0441 \u043a\u043e\u0434\u043e\u043c ** ` {code} ` **.",
|
"description": "\u0427\u0442\u043e\u0431\u044b \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u0443\u044e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0434\u043d\u043e\u0440\u0430\u0437\u043e\u0432\u044b\u0445 \u043f\u0430\u0440\u043e\u043b\u0435\u0439, \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043e\u0442\u0441\u043a\u0430\u043d\u0438\u0440\u0443\u0439\u0442\u0435 QR-\u043a\u043e\u0434 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0433\u043e \u043d\u0435\u0442, \u043c\u044b \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u0438\u0431\u043e [Google Authenticator](https://support.google.com/accounts/answer/1066447), \u043b\u0438\u0431\u043e [Authy](https://authy.com/). \n\n {qr_code} \n \n\u041f\u043e\u0441\u043b\u0435 \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f QR-\u043a\u043e\u0434\u0430 \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u0448\u0435\u0441\u0442\u0438\u0437\u043d\u0430\u0447\u043d\u044b\u0439 \u043a\u043e\u0434 \u0438\u0437 \u0432\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441\u043e \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c QR-\u043a\u043e\u0434\u0430, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0434\u0430 **`{code}`**.",
|
||||||
"title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c TOTP"
|
"title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0434\u0432\u0443\u0445\u0444\u0430\u043a\u0442\u043e\u0440\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c TOTP"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
16
homeassistant/components/auth/.translations/sl.json
Normal file
16
homeassistant/components/auth/.translations/sl.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"mfa_setup": {
|
||||||
|
"totp": {
|
||||||
|
"error": {
|
||||||
|
"invalid_code": "Neveljavna koda, prosimo, poskusite znova. \u010ce dobite to sporo\u010dilo ve\u010dkrat, prosimo poskrbite, da bo ura va\u0161ega Home Assistenta to\u010dna."
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"description": "\u010ce \u017eelite aktivirati preverjanje pristnosti dveh faktorjev z enkratnimi gesli, ki temeljijo na \u010dasu, skenirajte kodo QR s svojo aplikacijo za preverjanje pristnosti. \u010ce je nimate, priporo\u010damo bodisi [Google Authenticator] (https://support.google.com/accounts/answer/1066447) ali [Authy] (https://authy.com/). \n\n {qr_code} \n \n Po skeniranju kode vnesite \u0161estmestno kodo iz aplikacije, da preverite nastavitev. \u010ce imate te\u017eave pri skeniranju kode QR, naredite ro\u010dno nastavitev s kodo ** ` {code} ` **.",
|
||||||
|
"title": "Nastavite dvofaktorsko avtentifikacijo s pomo\u010djo TOTP-ja"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "TOTP"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -54,6 +54,11 @@ class DeconzBinarySensor(BinarySensorDevice):
|
|||||||
self._sensor.register_async_callback(self.async_update_callback)
|
self._sensor.register_async_callback(self.async_update_callback)
|
||||||
self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._sensor.deconz_id
|
self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._sensor.deconz_id
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
|
"""Disconnect sensor object when removed."""
|
||||||
|
self._sensor.remove_callback(self.async_update_callback)
|
||||||
|
self._sensor = None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self, reason):
|
def async_update_callback(self, reason):
|
||||||
"""Update the sensor's state.
|
"""Update the sensor's state.
|
||||||
|
@ -374,11 +374,11 @@ class XiaomiCube(XiaomiBinarySensor):
|
|||||||
self._last_action = None
|
self._last_action = None
|
||||||
self._state = False
|
self._state = False
|
||||||
if 'proto' not in device or int(device['proto'][0:1]) == 1:
|
if 'proto' not in device or int(device['proto'][0:1]) == 1:
|
||||||
self._data_key = 'status'
|
data_key = 'status'
|
||||||
else:
|
else:
|
||||||
self._data_key = 'cube_status'
|
data_key = 'cube_status'
|
||||||
XiaomiBinarySensor.__init__(self, device, 'Cube', xiaomi_hub,
|
XiaomiBinarySensor.__init__(self, device, 'Cube', xiaomi_hub,
|
||||||
None, None)
|
data_key, None)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
|
13
homeassistant/components/cast/.translations/fr.json
Normal file
13
homeassistant/components/cast/.translations/fr.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"no_devices_found": "Aucun appareil Google Cast trouv\u00e9 sur le r\u00e9seau.",
|
||||||
|
"single_instance_allowed": "Seulement une seule configuration de Google Cast est n\u00e9cessaire."
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"confirm": {
|
||||||
|
"description": "Voulez-vous configurer Google Cast?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
73
homeassistant/components/cover/insteon.py
Normal file
73
homeassistant/components/cover/insteon.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
"""
|
||||||
|
Support for Insteon covers via PowerLinc Modem.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/cover.insteon/
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
|
||||||
|
from homeassistant.components.insteon import InsteonEntity
|
||||||
|
from homeassistant.components.cover import (CoverDevice, ATTR_POSITION,
|
||||||
|
SUPPORT_OPEN, SUPPORT_CLOSE,
|
||||||
|
SUPPORT_SET_POSITION)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DEPENDENCIES = ['insteon']
|
||||||
|
SUPPORTED_FEATURES = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_platform(hass, config, async_add_entities,
|
||||||
|
discovery_info=None):
|
||||||
|
"""Set up the Insteon platform."""
|
||||||
|
if not discovery_info:
|
||||||
|
return
|
||||||
|
|
||||||
|
insteon_modem = hass.data['insteon'].get('modem')
|
||||||
|
|
||||||
|
address = discovery_info['address']
|
||||||
|
device = insteon_modem.devices[address]
|
||||||
|
state_key = discovery_info['state_key']
|
||||||
|
|
||||||
|
_LOGGER.debug('Adding device %s entity %s to Cover platform',
|
||||||
|
device.address.hex, device.states[state_key].name)
|
||||||
|
|
||||||
|
new_entity = InsteonCoverDevice(device, state_key)
|
||||||
|
|
||||||
|
async_add_entities([new_entity])
|
||||||
|
|
||||||
|
|
||||||
|
class InsteonCoverDevice(InsteonEntity, CoverDevice):
|
||||||
|
"""A Class for an Insteon device."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_cover_position(self):
|
||||||
|
"""Return the current cover position."""
|
||||||
|
return int(math.ceil(self._insteon_device_state.value*100/255))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported_features(self):
|
||||||
|
"""Return the supported features for this entity."""
|
||||||
|
return SUPPORTED_FEATURES
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_closed(self):
|
||||||
|
"""Return the boolean response if the node is on."""
|
||||||
|
return bool(self.current_cover_position)
|
||||||
|
|
||||||
|
async def async_open_cover(self, **kwargs):
|
||||||
|
"""Open device."""
|
||||||
|
self._insteon_device_state.open()
|
||||||
|
|
||||||
|
async def async_close_cover(self, **kwargs):
|
||||||
|
"""Close device."""
|
||||||
|
self._insteon_device_state.close()
|
||||||
|
|
||||||
|
async def async_set_cover_position(self, **kwargs):
|
||||||
|
"""Set the cover position."""
|
||||||
|
position = int(kwargs[ATTR_POSITION]*255/100)
|
||||||
|
if position == 0:
|
||||||
|
self._insteon_device_state.close()
|
||||||
|
else:
|
||||||
|
self._insteon_device_state.set_position(position)
|
@ -120,6 +120,11 @@ class MyQDevice(CoverDevice):
|
|||||||
"""Flag supported features."""
|
"""Flag supported features."""
|
||||||
return SUPPORT_OPEN | SUPPORT_CLOSE
|
return SUPPORT_OPEN | SUPPORT_CLOSE
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
"""Return a unique, HASS-friendly identifier for this entity."""
|
||||||
|
return self.device_id
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Update status of cover."""
|
"""Update status of cover."""
|
||||||
self._status = self.myq.get_status(self.device_id)
|
self._status = self.myq.get_status(self.device_id)
|
||||||
|
@ -28,6 +28,6 @@
|
|||||||
"title": "Extra configuratieopties voor deCONZ"
|
"title": "Extra configuratieopties voor deCONZ"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"title": "deCONZ"
|
"title": "deCONZ Zigbee gateway"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -24,7 +24,7 @@ from .const import (
|
|||||||
CONF_ALLOW_CLIP_SENSOR, CONFIG_FILE, DATA_DECONZ_EVENT,
|
CONF_ALLOW_CLIP_SENSOR, CONFIG_FILE, DATA_DECONZ_EVENT,
|
||||||
DATA_DECONZ_ID, DATA_DECONZ_UNSUB, DOMAIN, _LOGGER)
|
DATA_DECONZ_ID, DATA_DECONZ_UNSUB, DOMAIN, _LOGGER)
|
||||||
|
|
||||||
REQUIREMENTS = ['pydeconz==44']
|
REQUIREMENTS = ['pydeconz==45']
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema({
|
CONFIG_SCHEMA = vol.Schema({
|
||||||
DOMAIN: vol.Schema({
|
DOMAIN: vol.Schema({
|
||||||
@ -179,15 +179,22 @@ async def async_unload_entry(hass, config_entry):
|
|||||||
deconz = hass.data.pop(DOMAIN)
|
deconz = hass.data.pop(DOMAIN)
|
||||||
hass.services.async_remove(DOMAIN, SERVICE_DECONZ)
|
hass.services.async_remove(DOMAIN, SERVICE_DECONZ)
|
||||||
deconz.close()
|
deconz.close()
|
||||||
for component in ['binary_sensor', 'light', 'scene', 'sensor']:
|
|
||||||
|
for component in ['binary_sensor', 'light', 'scene', 'sensor', 'switch']:
|
||||||
await hass.config_entries.async_forward_entry_unload(
|
await hass.config_entries.async_forward_entry_unload(
|
||||||
config_entry, component)
|
config_entry, component)
|
||||||
|
|
||||||
dispatchers = hass.data[DATA_DECONZ_UNSUB]
|
dispatchers = hass.data[DATA_DECONZ_UNSUB]
|
||||||
for unsub_dispatcher in dispatchers:
|
for unsub_dispatcher in dispatchers:
|
||||||
unsub_dispatcher()
|
unsub_dispatcher()
|
||||||
hass.data[DATA_DECONZ_UNSUB] = []
|
hass.data[DATA_DECONZ_UNSUB] = []
|
||||||
hass.data[DATA_DECONZ_EVENT] = []
|
|
||||||
|
for event in hass.data[DATA_DECONZ_EVENT]:
|
||||||
|
event.async_will_remove_from_hass()
|
||||||
|
hass.data[DATA_DECONZ_EVENT].remove(event)
|
||||||
|
|
||||||
hass.data[DATA_DECONZ_ID] = []
|
hass.data[DATA_DECONZ_ID] = []
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ -206,6 +213,12 @@ class DeconzEvent:
|
|||||||
self._event = 'deconz_{}'.format(CONF_EVENT)
|
self._event = 'deconz_{}'.format(CONF_EVENT)
|
||||||
self._id = slugify(self._device.name)
|
self._id = slugify(self._device.name)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_will_remove_from_hass(self) -> None:
|
||||||
|
"""Disconnect event object when removed."""
|
||||||
|
self._device.remove_callback(self.async_update_callback)
|
||||||
|
self._device = None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self, reason):
|
def async_update_callback(self, reason):
|
||||||
"""Fire the event if reason is that state is updated."""
|
"""Fire the event if reason is that state is updated."""
|
||||||
|
@ -143,7 +143,7 @@ class FeedManager:
|
|||||||
else:
|
else:
|
||||||
self._has_published_parsed = False
|
self._has_published_parsed = False
|
||||||
_LOGGER.debug("No published_parsed info available for entry %s",
|
_LOGGER.debug("No published_parsed info available for entry %s",
|
||||||
entry.title)
|
entry)
|
||||||
entry.update({'feed_url': self._url})
|
entry.update({'feed_url': self._url})
|
||||||
self._hass.bus.fire(self._event_type, entry)
|
self._hass.bus.fire(self._event_type, entry)
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ class FeedManager:
|
|||||||
self._update_and_fire_entry(entry)
|
self._update_and_fire_entry(entry)
|
||||||
new_entries = True
|
new_entries = True
|
||||||
else:
|
else:
|
||||||
_LOGGER.debug("Entry %s already processed", entry.title)
|
_LOGGER.debug("Entry %s already processed", entry)
|
||||||
if not new_entries:
|
if not new_entries:
|
||||||
self._log_no_entries()
|
self._log_no_entries()
|
||||||
self._firstrun = False
|
self._firstrun = False
|
||||||
|
@ -26,7 +26,7 @@ from homeassistant.helpers.translation import async_get_translations
|
|||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
from homeassistant.util.yaml import load_yaml
|
from homeassistant.util.yaml import load_yaml
|
||||||
|
|
||||||
REQUIREMENTS = ['home-assistant-frontend==20180829.0']
|
REQUIREMENTS = ['home-assistant-frontend==20180831.0']
|
||||||
|
|
||||||
DOMAIN = 'frontend'
|
DOMAIN = 'frontend'
|
||||||
DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log',
|
DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log',
|
||||||
|
68
homeassistant/components/geo_location/__init__.py
Normal file
68
homeassistant/components/geo_location/__init__.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
"""
|
||||||
|
Geo Location component.
|
||||||
|
|
||||||
|
This component covers platforms that deal with external events that contain
|
||||||
|
a geo location related to the installed HA instance.
|
||||||
|
|
||||||
|
For more details about this component, please refer to the documentation at
|
||||||
|
https://home-assistant.io/components/geo_location/
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
from datetime import timedelta
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ATTR_DISTANCE = 'distance'
|
||||||
|
DOMAIN = 'geo_location'
|
||||||
|
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||||
|
GROUP_NAME_ALL_EVENTS = 'All Geo Location Events'
|
||||||
|
SCAN_INTERVAL = timedelta(seconds=60)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup(hass, config):
|
||||||
|
"""Set up this component."""
|
||||||
|
component = EntityComponent(
|
||||||
|
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_EVENTS)
|
||||||
|
await component.async_setup(config)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class GeoLocationEvent(Entity):
|
||||||
|
"""This represents an external event with an associated geo location."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
"""Return the state of the sensor."""
|
||||||
|
if self.distance is not None:
|
||||||
|
return round(self.distance, 1)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def distance(self) -> Optional[float]:
|
||||||
|
"""Return distance value of this external event."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def latitude(self) -> Optional[float]:
|
||||||
|
"""Return latitude value of this external event."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def longitude(self) -> Optional[float]:
|
||||||
|
"""Return longitude value of this external event."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state_attributes(self):
|
||||||
|
"""Return the state attributes of this external event."""
|
||||||
|
data = {}
|
||||||
|
if self.latitude is not None:
|
||||||
|
data[ATTR_LATITUDE] = round(self.latitude, 5)
|
||||||
|
if self.longitude is not None:
|
||||||
|
data[ATTR_LONGITUDE] = round(self.longitude, 5)
|
||||||
|
return data
|
132
homeassistant/components/geo_location/demo.py
Normal file
132
homeassistant/components/geo_location/demo.py
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
"""
|
||||||
|
Demo platform for the geo location component.
|
||||||
|
|
||||||
|
For more details about this platform, please refer to the documentation
|
||||||
|
https://home-assistant.io/components/demo/
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import random
|
||||||
|
from datetime import timedelta
|
||||||
|
from math import pi, cos, sin, radians
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from homeassistant.components.geo_location import GeoLocationEvent
|
||||||
|
from homeassistant.helpers.event import track_time_interval
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
AVG_KM_PER_DEGREE = 111.0
|
||||||
|
DEFAULT_UNIT_OF_MEASUREMENT = "km"
|
||||||
|
DEFAULT_UPDATE_INTERVAL = timedelta(minutes=1)
|
||||||
|
MAX_RADIUS_IN_KM = 50
|
||||||
|
NUMBER_OF_DEMO_DEVICES = 5
|
||||||
|
|
||||||
|
EVENT_NAMES = ["Bushfire", "Hazard Reduction", "Grass Fire", "Burn off",
|
||||||
|
"Structure Fire", "Fire Alarm", "Thunderstorm", "Tornado",
|
||||||
|
"Cyclone", "Waterspout", "Dust Storm", "Blizzard", "Ice Storm",
|
||||||
|
"Earthquake", "Tsunami"]
|
||||||
|
|
||||||
|
|
||||||
|
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||||
|
"""Set up the Demo geo locations."""
|
||||||
|
DemoManager(hass, add_entities)
|
||||||
|
|
||||||
|
|
||||||
|
class DemoManager:
|
||||||
|
"""Device manager for demo geo location events."""
|
||||||
|
|
||||||
|
def __init__(self, hass, add_entities):
|
||||||
|
"""Initialise the demo geo location event manager."""
|
||||||
|
self._hass = hass
|
||||||
|
self._add_entities = add_entities
|
||||||
|
self._managed_devices = []
|
||||||
|
self._update(count=NUMBER_OF_DEMO_DEVICES)
|
||||||
|
self._init_regular_updates()
|
||||||
|
|
||||||
|
def _generate_random_event(self):
|
||||||
|
"""Generate a random event in vicinity of this HA instance."""
|
||||||
|
home_latitude = self._hass.config.latitude
|
||||||
|
home_longitude = self._hass.config.longitude
|
||||||
|
|
||||||
|
# Approx. 111km per degree (north-south).
|
||||||
|
radius_in_degrees = random.random() * MAX_RADIUS_IN_KM / \
|
||||||
|
AVG_KM_PER_DEGREE
|
||||||
|
radius_in_km = radius_in_degrees * AVG_KM_PER_DEGREE
|
||||||
|
angle = random.random() * 2 * pi
|
||||||
|
# Compute coordinates based on radius and angle. Adjust longitude value
|
||||||
|
# based on HA's latitude.
|
||||||
|
latitude = home_latitude + radius_in_degrees * sin(angle)
|
||||||
|
longitude = home_longitude + radius_in_degrees * cos(angle) / \
|
||||||
|
cos(radians(home_latitude))
|
||||||
|
|
||||||
|
event_name = random.choice(EVENT_NAMES)
|
||||||
|
return DemoGeoLocationEvent(event_name, radius_in_km, latitude,
|
||||||
|
longitude, DEFAULT_UNIT_OF_MEASUREMENT)
|
||||||
|
|
||||||
|
def _init_regular_updates(self):
|
||||||
|
"""Schedule regular updates based on configured time interval."""
|
||||||
|
track_time_interval(self._hass, lambda now: self._update(),
|
||||||
|
DEFAULT_UPDATE_INTERVAL)
|
||||||
|
|
||||||
|
def _update(self, count=1):
|
||||||
|
"""Remove events and add new random events."""
|
||||||
|
# Remove devices.
|
||||||
|
for _ in range(1, count + 1):
|
||||||
|
if self._managed_devices:
|
||||||
|
device = random.choice(self._managed_devices)
|
||||||
|
if device:
|
||||||
|
_LOGGER.debug("Removing %s", device)
|
||||||
|
self._managed_devices.remove(device)
|
||||||
|
self._hass.add_job(device.async_remove())
|
||||||
|
# Generate new devices from events.
|
||||||
|
new_devices = []
|
||||||
|
for _ in range(1, count + 1):
|
||||||
|
new_device = self._generate_random_event()
|
||||||
|
_LOGGER.debug("Adding %s", new_device)
|
||||||
|
new_devices.append(new_device)
|
||||||
|
self._managed_devices.append(new_device)
|
||||||
|
self._add_entities(new_devices)
|
||||||
|
|
||||||
|
|
||||||
|
class DemoGeoLocationEvent(GeoLocationEvent):
|
||||||
|
"""This represents a demo geo location event."""
|
||||||
|
|
||||||
|
def __init__(self, name, distance, latitude, longitude,
|
||||||
|
unit_of_measurement):
|
||||||
|
"""Initialize entity with data provided."""
|
||||||
|
self._name = name
|
||||||
|
self._distance = distance
|
||||||
|
self._latitude = latitude
|
||||||
|
self._longitude = longitude
|
||||||
|
self._unit_of_measurement = unit_of_measurement
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> Optional[str]:
|
||||||
|
"""Return the name of the event."""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self):
|
||||||
|
"""No polling needed for a demo geo location event."""
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def distance(self) -> Optional[float]:
|
||||||
|
"""Return distance value of this external event."""
|
||||||
|
return self._distance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def latitude(self) -> Optional[float]:
|
||||||
|
"""Return latitude value of this external event."""
|
||||||
|
return self._latitude
|
||||||
|
|
||||||
|
@property
|
||||||
|
def longitude(self) -> Optional[float]:
|
||||||
|
"""Return longitude value of this external event."""
|
||||||
|
return self._longitude
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_of_measurement(self):
|
||||||
|
"""Return the unit of measurement."""
|
||||||
|
return self._unit_of_measurement
|
@ -5,6 +5,7 @@
|
|||||||
"unknown": "Ein unbekannter Fehler ist aufgetreten."
|
"unknown": "Ein unbekannter Fehler ist aufgetreten."
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
|
"invalid_2fa": "Ung\u00fcltige 2-Faktor Authentifizierung, bitte versuche es erneut.",
|
||||||
"invalid_2fa_method": "Ung\u00fcltige 2FA Methode (mit Telefon verifizieren)",
|
"invalid_2fa_method": "Ung\u00fcltige 2FA Methode (mit Telefon verifizieren)",
|
||||||
"invalid_login": "Ung\u00fcltige Daten, bitte erneut versuchen."
|
"invalid_login": "Ung\u00fcltige Daten, bitte erneut versuchen."
|
||||||
},
|
},
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"unknown": "Unknown error occurred."
|
"unknown": "Unknown error occurred."
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"invalid_2fa": "Invalid 2 Factor Authorization, please try again.",
|
"invalid_2fa": "Invalid 2 Factor Authentication, please try again.",
|
||||||
"invalid_2fa_method": "Invalid 2FA Method (Verify on Phone).",
|
"invalid_2fa_method": "Invalid 2FA Method (Verify on Phone).",
|
||||||
"invalid_login": "Invalid Login, please try again."
|
"invalid_login": "Invalid Login, please try again."
|
||||||
},
|
},
|
||||||
@ -14,7 +14,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"2fa": "2FA Pin"
|
"2fa": "2FA Pin"
|
||||||
},
|
},
|
||||||
"title": "2-Factor-Authorization"
|
"title": "2-Factor-Authentication"
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"data": {
|
"data": {
|
||||||
|
18
homeassistant/components/hangouts/.translations/es-419.json
Normal file
18
homeassistant/components/hangouts/.translations/es-419.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "Google Hangouts ya est\u00e1 configurado",
|
||||||
|
"unknown": "Se produjo un error desconocido."
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"email": "Direcci\u00f3n de correo electr\u00f3nico",
|
||||||
|
"password": "Contrase\u00f1a"
|
||||||
|
},
|
||||||
|
"title": "Inicio de sesi\u00f3n de Google Hangouts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "Google Hangouts"
|
||||||
|
}
|
||||||
|
}
|
21
homeassistant/components/hangouts/.translations/fr.json
Normal file
21
homeassistant/components/hangouts/.translations/fr.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "Google Hangouts est d\u00e9j\u00e0 configur\u00e9",
|
||||||
|
"unknown": "Une erreur inconnue s'est produite"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"invalid_login": "Login invalide, veuillez r\u00e9essayer."
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"2fa": {
|
||||||
|
"title": "Authentification \u00e0 2 facteurs"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"password": "Mot de passe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,29 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "Google Hangouts \u00e8 gi\u00e0 configurato",
|
||||||
|
"unknown": "Si \u00e8 verificato un errore sconosciuto."
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"invalid_2fa": "Autenticazione a 2 fattori non valida, riprovare.",
|
||||||
|
"invalid_2fa_method": "Metodo 2FA non valido (verifica sul telefono).",
|
||||||
|
"invalid_login": "Accesso non valido, si prega di riprovare."
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"2fa": {
|
||||||
|
"data": {
|
||||||
|
"2fa": "2FA Pin"
|
||||||
|
},
|
||||||
|
"title": "Autenticazione a due fattori"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"email": "Indirizzo email",
|
||||||
|
"password": "Password"
|
||||||
|
},
|
||||||
|
"title": "Accesso a Google Hangouts"
|
||||||
|
}
|
||||||
|
},
|
||||||
"title": "Google Hangouts"
|
"title": "Google Hangouts"
|
||||||
}
|
}
|
||||||
}
|
}
|
29
homeassistant/components/hangouts/.translations/nl.json
Normal file
29
homeassistant/components/hangouts/.translations/nl.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "Google Hangouts is al geconfigureerd",
|
||||||
|
"unknown": "Onbekende fout opgetreden."
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"invalid_2fa": "Ongeldige twee-factor-authenticatie, probeer het opnieuw.",
|
||||||
|
"invalid_2fa_method": "Ongeldige 2FA-methode (verifi\u00ebren op telefoon).",
|
||||||
|
"invalid_login": "Ongeldige aanmelding, probeer het opnieuw."
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"2fa": {
|
||||||
|
"data": {
|
||||||
|
"2fa": "2FA pin"
|
||||||
|
},
|
||||||
|
"title": "Twee-factor-authenticatie"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"email": "E-mailadres",
|
||||||
|
"password": "Wachtwoord"
|
||||||
|
},
|
||||||
|
"title": "Google Hangouts inlog"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "Google Hangouts"
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,27 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "Google Hangouts er allerede konfigurert",
|
||||||
|
"unknown": "Ukjent feil oppstod."
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"invalid_2fa": "Ugyldig tofaktorautentisering, vennligst pr\u00f8v igjen.",
|
||||||
|
"invalid_2fa_method": "Ugyldig 2FA-metode (Bekreft p\u00e5 telefon).",
|
||||||
|
"invalid_login": "Ugyldig innlogging, vennligst pr\u00f8v igjen."
|
||||||
|
},
|
||||||
"step": {
|
"step": {
|
||||||
|
"2fa": {
|
||||||
|
"data": {
|
||||||
|
"2fa": "2FA Pin"
|
||||||
|
},
|
||||||
|
"title": "Tofaktorautentisering"
|
||||||
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"data": {
|
"data": {
|
||||||
"email": "E-postadresse",
|
"email": "E-postadresse",
|
||||||
"password": "Passord"
|
"password": "Passord"
|
||||||
}
|
},
|
||||||
|
"title": "Google Hangouts p\u00e5logging"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"title": "Google Hangouts"
|
"title": "Google Hangouts"
|
||||||
|
@ -1,23 +1,14 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "Hangouts do Google j\u00e1 est\u00e1 configurado.",
|
"already_configured": "Hangouts do Google j\u00e1 est\u00e1 configurado."
|
||||||
"unknown": "Ocorreu um erro desconhecido."
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"invalid_2fa_method": "M\u00e9todo 2FA inv\u00e1lido (verificar no telefone).",
|
|
||||||
"invalid_login": "Login inv\u00e1lido, por favor, tente novamente."
|
|
||||||
},
|
},
|
||||||
"step": {
|
"step": {
|
||||||
"2fa": {
|
"2fa": {
|
||||||
"data": {
|
|
||||||
"2fa": "Pin 2FA"
|
|
||||||
},
|
|
||||||
"title": ""
|
"title": ""
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"data": {
|
"data": {
|
||||||
"email": "Endere\u00e7o de e-mail",
|
|
||||||
"password": "Senha"
|
"password": "Senha"
|
||||||
},
|
},
|
||||||
"title": "Login do Hangouts do Google"
|
"title": "Login do Hangouts do Google"
|
||||||
|
31
homeassistant/components/hangouts/.translations/pt.json
Normal file
31
homeassistant/components/hangouts/.translations/pt.json
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "Google Hangouts j\u00e1 est\u00e1 configurado",
|
||||||
|
"unknown": "Ocorreu um erro desconhecido."
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"invalid_2fa": "Autoriza\u00e7\u00e3o por 2 factores inv\u00e1lida, por favor, tente novamente.",
|
||||||
|
"invalid_2fa_method": "M\u00e9todo 2FA inv\u00e1lido (verificar no telefone).",
|
||||||
|
"invalid_login": "Login inv\u00e1lido, por favor, tente novamente."
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"2fa": {
|
||||||
|
"data": {
|
||||||
|
"2fa": "Pin 2FA"
|
||||||
|
},
|
||||||
|
"description": "Vazio",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"email": "Endere\u00e7o de e-mail",
|
||||||
|
"password": "Palavra-passe"
|
||||||
|
},
|
||||||
|
"description": "Vazio",
|
||||||
|
"title": "Login Google Hangouts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": ""
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "Accesspoint ya est\u00e1 configurado",
|
"already_configured": "Accesspoint ya est\u00e1 configurado",
|
||||||
"conection_aborted": "No se pudo conectar al servidor HMIP",
|
"conection_aborted": "No se pudo conectar al servidor HMIP",
|
||||||
|
"connection_aborted": "No se pudo conectar al servidor HMIP",
|
||||||
"unknown": "Se produjo un error desconocido."
|
"unknown": "Se produjo un error desconocido."
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@ -18,6 +19,7 @@
|
|||||||
"pin": "C\u00f3digo PIN (opcional)"
|
"pin": "C\u00f3digo PIN (opcional)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"title": "HomematicIP Cloud"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"unknown": "Une erreur inconnue s'est produite"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"invalid_pin": "Code PIN invalide, veuillez r\u00e9essayer.",
|
||||||
|
"press_the_button": "Veuillez appuyer sur le bouton bleu.",
|
||||||
|
"register_failed": "\u00c9chec d'enregistrement. Veuillez r\u00e9essayer."
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"hapid": "ID du point d'acc\u00e8s (SGTIN)",
|
||||||
|
"name": "Nom (facultatif, utilis\u00e9 comme pr\u00e9fixe de nom pour tous les p\u00e9riph\u00e9riques)",
|
||||||
|
"pin": "Code PIN (facultatif)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,13 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "Il punto di accesso \u00e8 gi\u00e0 configurato",
|
||||||
|
"connection_aborted": "Impossibile connettersi al server HMIP"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"press_the_button": "Si prega di premere il pulsante blu.",
|
||||||
|
"register_failed": "Registrazione fallita, si prega di riprovare."
|
||||||
|
},
|
||||||
"step": {
|
"step": {
|
||||||
"init": {
|
"init": {
|
||||||
"data": {
|
"data": {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "Accesspoint is reeds geconfigureerd",
|
"already_configured": "Accesspoint is al geconfigureerd",
|
||||||
"conection_aborted": "Kon geen verbinding maken met de HMIP-server",
|
"conection_aborted": "Kon geen verbinding maken met de HMIP-server",
|
||||||
"connection_aborted": "Kon geen verbinding maken met de HMIP-server",
|
"connection_aborted": "Kon geen verbinding maken met de HMIP-server",
|
||||||
"unknown": "Er is een onbekende fout opgetreden."
|
"unknown": "Er is een onbekende fout opgetreden."
|
||||||
@ -19,11 +19,11 @@
|
|||||||
"name": "Naam (optioneel, gebruikt als naamprefix voor alle apparaten)",
|
"name": "Naam (optioneel, gebruikt als naamprefix voor alle apparaten)",
|
||||||
"pin": "Pin-Code (optioneel)"
|
"pin": "Pin-Code (optioneel)"
|
||||||
},
|
},
|
||||||
"title": "Kies HomematicIP Accesspoint"
|
"title": "Kies HomematicIP accesspoint"
|
||||||
},
|
},
|
||||||
"link": {
|
"link": {
|
||||||
"description": "Druk op de blauwe knop op de accesspoint en de verzendknop om HomematicIP met de Home Assistant te registreren. \n\n",
|
"description": "Druk op de blauwe knop op het accesspoint en de verzendknop om HomematicIP bij Home Assistant te registreren. \n\n",
|
||||||
"title": "Link Accesspoint"
|
"title": "Link accesspoint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"title": "HomematicIP Cloud"
|
"title": "HomematicIP Cloud"
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "O ponto de acesso j\u00e1 se encontra configurado",
|
"already_configured": "O ponto de acesso j\u00e1 se encontra configurado",
|
||||||
"conection_aborted": "N\u00e3o foi poss\u00edvel ligar ao servidor HMIP",
|
"conection_aborted": "N\u00e3o foi poss\u00edvel ligar ao servidor HMIP",
|
||||||
|
"connection_aborted": "N\u00e3o foi poss\u00edvel ligar ao servidor HMIP",
|
||||||
"unknown": "Ocorreu um erro desconhecido."
|
"unknown": "Ocorreu um erro desconhecido."
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
|
@ -24,6 +24,6 @@
|
|||||||
"title": "Link Hub"
|
"title": "Link Hub"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"title": "Philips Hue Bridge"
|
"title": "Philips Hue"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,7 +18,7 @@ import homeassistant.helpers.config_validation as cv
|
|||||||
from homeassistant.helpers import discovery
|
from homeassistant.helpers import discovery
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
REQUIREMENTS = ['insteonplm==0.12.3']
|
REQUIREMENTS = ['insteonplm==0.13.1']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -358,6 +358,8 @@ class IPDB:
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Create the INSTEON Product Database (IPDB)."""
|
"""Create the INSTEON Product Database (IPDB)."""
|
||||||
|
from insteonplm.states.cover import Cover
|
||||||
|
|
||||||
from insteonplm.states.onOff import (OnOffSwitch,
|
from insteonplm.states.onOff import (OnOffSwitch,
|
||||||
OnOffSwitch_OutletTop,
|
OnOffSwitch_OutletTop,
|
||||||
OnOffSwitch_OutletBottom,
|
OnOffSwitch_OutletBottom,
|
||||||
@ -383,7 +385,9 @@ class IPDB:
|
|||||||
X10AllLightsOnSensor,
|
X10AllLightsOnSensor,
|
||||||
X10AllLightsOffSensor)
|
X10AllLightsOffSensor)
|
||||||
|
|
||||||
self.states = [State(OnOffSwitch_OutletTop, 'switch'),
|
self.states = [State(Cover, 'cover'),
|
||||||
|
|
||||||
|
State(OnOffSwitch_OutletTop, 'switch'),
|
||||||
State(OnOffSwitch_OutletBottom, 'switch'),
|
State(OnOffSwitch_OutletBottom, 'switch'),
|
||||||
State(OpenClosedRelay, 'switch'),
|
State(OpenClosedRelay, 'switch'),
|
||||||
State(OnOffSwitch, 'switch'),
|
State(OnOffSwitch, 'switch'),
|
||||||
@ -470,11 +474,10 @@ class InsteonEntity(Entity):
|
|||||||
return attributes
|
return attributes
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_entity_update(self, deviceid, statename, val):
|
def async_entity_update(self, deviceid, group, val):
|
||||||
"""Receive notification from transport that new data exists."""
|
"""Receive notification from transport that new data exists."""
|
||||||
_LOGGER.debug('Received update for device %s group %d statename %s',
|
_LOGGER.debug('Received update for device %s group %d value %s',
|
||||||
self.address, self.group,
|
deviceid.human, group, val)
|
||||||
self._insteon_device_state.name)
|
|
||||||
self.async_schedule_update_ha_state()
|
self.async_schedule_update_ha_state()
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
@ -82,6 +82,11 @@ class DeconzLight(Light):
|
|||||||
self._light.register_async_callback(self.async_update_callback)
|
self._light.register_async_callback(self.async_update_callback)
|
||||||
self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._light.deconz_id
|
self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._light.deconz_id
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
|
"""Disconnect light object when removed."""
|
||||||
|
self._light.remove_callback(self.async_update_callback)
|
||||||
|
self._light = None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self, reason):
|
def async_update_callback(self, reason):
|
||||||
"""Update the light's state."""
|
"""Update the light's state."""
|
||||||
|
@ -167,9 +167,9 @@ async def async_setup_platform(hass,
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def lifx_features(device):
|
def lifx_features(bulb):
|
||||||
"""Return a feature map for this device, or a default map if unknown."""
|
"""Return a feature map for this bulb, or a default map if unknown."""
|
||||||
return aiolifx().products.features_map.get(device.product) or \
|
return aiolifx().products.features_map.get(bulb.product) or \
|
||||||
aiolifx().products.features_map.get(1)
|
aiolifx().products.features_map.get(1)
|
||||||
|
|
||||||
|
|
||||||
@ -256,7 +256,7 @@ class LIFXManager:
|
|||||||
|
|
||||||
async def start_effect(self, entities, service, **kwargs):
|
async def start_effect(self, entities, service, **kwargs):
|
||||||
"""Start a light effect on entities."""
|
"""Start a light effect on entities."""
|
||||||
devices = [light.device for light in entities]
|
bulbs = [light.bulb for light in entities]
|
||||||
|
|
||||||
if service == SERVICE_EFFECT_PULSE:
|
if service == SERVICE_EFFECT_PULSE:
|
||||||
effect = aiolifx_effects().EffectPulse(
|
effect = aiolifx_effects().EffectPulse(
|
||||||
@ -266,7 +266,7 @@ class LIFXManager:
|
|||||||
mode=kwargs.get(ATTR_MODE),
|
mode=kwargs.get(ATTR_MODE),
|
||||||
hsbk=find_hsbk(**kwargs),
|
hsbk=find_hsbk(**kwargs),
|
||||||
)
|
)
|
||||||
await self.effects_conductor.start(effect, devices)
|
await self.effects_conductor.start(effect, bulbs)
|
||||||
elif service == SERVICE_EFFECT_COLORLOOP:
|
elif service == SERVICE_EFFECT_COLORLOOP:
|
||||||
preprocess_turn_on_alternatives(kwargs)
|
preprocess_turn_on_alternatives(kwargs)
|
||||||
|
|
||||||
@ -282,12 +282,12 @@ class LIFXManager:
|
|||||||
transition=kwargs.get(ATTR_TRANSITION),
|
transition=kwargs.get(ATTR_TRANSITION),
|
||||||
brightness=brightness,
|
brightness=brightness,
|
||||||
)
|
)
|
||||||
await self.effects_conductor.start(effect, devices)
|
await self.effects_conductor.start(effect, bulbs)
|
||||||
elif service == SERVICE_EFFECT_STOP:
|
elif service == SERVICE_EFFECT_STOP:
|
||||||
await self.effects_conductor.stop(devices)
|
await self.effects_conductor.stop(bulbs)
|
||||||
|
|
||||||
def service_to_entities(self, service):
|
def service_to_entities(self, service):
|
||||||
"""Return the known devices that a service call mentions."""
|
"""Return the known entities that a service call mentions."""
|
||||||
entity_ids = extract_entity_ids(self.hass, service)
|
entity_ids = extract_entity_ids(self.hass, service)
|
||||||
if entity_ids:
|
if entity_ids:
|
||||||
entities = [entity for entity in self.entities.values()
|
entities = [entity for entity in self.entities.values()
|
||||||
@ -298,50 +298,50 @@ class LIFXManager:
|
|||||||
return entities
|
return entities
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def register(self, device):
|
def register(self, bulb):
|
||||||
"""Handle aiolifx detected bulb."""
|
"""Handle aiolifx detected bulb."""
|
||||||
self.hass.async_add_job(self.register_new_device(device))
|
self.hass.async_add_job(self.register_new_bulb(bulb))
|
||||||
|
|
||||||
async def register_new_device(self, device):
|
async def register_new_bulb(self, bulb):
|
||||||
"""Handle newly detected bulb."""
|
"""Handle newly detected bulb."""
|
||||||
if device.mac_addr in self.entities:
|
if bulb.mac_addr in self.entities:
|
||||||
entity = self.entities[device.mac_addr]
|
entity = self.entities[bulb.mac_addr]
|
||||||
entity.registered = True
|
entity.registered = True
|
||||||
_LOGGER.debug("%s register AGAIN", entity.who)
|
_LOGGER.debug("%s register AGAIN", entity.who)
|
||||||
await entity.update_hass()
|
await entity.update_hass()
|
||||||
else:
|
else:
|
||||||
_LOGGER.debug("%s register NEW", device.ip_addr)
|
_LOGGER.debug("%s register NEW", bulb.ip_addr)
|
||||||
|
|
||||||
# Read initial state
|
# Read initial state
|
||||||
ack = AwaitAioLIFX().wait
|
ack = AwaitAioLIFX().wait
|
||||||
color_resp = await ack(device.get_color)
|
color_resp = await ack(bulb.get_color)
|
||||||
if color_resp:
|
if color_resp:
|
||||||
version_resp = await ack(device.get_version)
|
version_resp = await ack(bulb.get_version)
|
||||||
|
|
||||||
if color_resp is None or version_resp is None:
|
if color_resp is None or version_resp is None:
|
||||||
_LOGGER.error("Failed to initialize %s", device.ip_addr)
|
_LOGGER.error("Failed to initialize %s", bulb.ip_addr)
|
||||||
device.registered = False
|
bulb.registered = False
|
||||||
else:
|
else:
|
||||||
device.timeout = MESSAGE_TIMEOUT
|
bulb.timeout = MESSAGE_TIMEOUT
|
||||||
device.retry_count = MESSAGE_RETRIES
|
bulb.retry_count = MESSAGE_RETRIES
|
||||||
device.unregister_timeout = UNAVAILABLE_GRACE
|
bulb.unregister_timeout = UNAVAILABLE_GRACE
|
||||||
|
|
||||||
if lifx_features(device)["multizone"]:
|
if lifx_features(bulb)["multizone"]:
|
||||||
entity = LIFXStrip(device, self.effects_conductor)
|
entity = LIFXStrip(bulb, self.effects_conductor)
|
||||||
elif lifx_features(device)["color"]:
|
elif lifx_features(bulb)["color"]:
|
||||||
entity = LIFXColor(device, self.effects_conductor)
|
entity = LIFXColor(bulb, self.effects_conductor)
|
||||||
else:
|
else:
|
||||||
entity = LIFXWhite(device, self.effects_conductor)
|
entity = LIFXWhite(bulb, self.effects_conductor)
|
||||||
|
|
||||||
_LOGGER.debug("%s register READY", entity.who)
|
_LOGGER.debug("%s register READY", entity.who)
|
||||||
self.entities[device.mac_addr] = entity
|
self.entities[bulb.mac_addr] = entity
|
||||||
self.async_add_entities([entity], True)
|
self.async_add_entities([entity], True)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def unregister(self, device):
|
def unregister(self, bulb):
|
||||||
"""Handle aiolifx disappearing bulbs."""
|
"""Handle aiolifx disappearing bulbs."""
|
||||||
if device.mac_addr in self.entities:
|
if bulb.mac_addr in self.entities:
|
||||||
entity = self.entities[device.mac_addr]
|
entity = self.entities[bulb.mac_addr]
|
||||||
_LOGGER.debug("%s unregister", entity.who)
|
_LOGGER.debug("%s unregister", entity.who)
|
||||||
entity.registered = False
|
entity.registered = False
|
||||||
self.hass.async_add_job(entity.async_update_ha_state())
|
self.hass.async_add_job(entity.async_update_ha_state())
|
||||||
@ -352,20 +352,17 @@ class AwaitAioLIFX:
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initialize the wrapper."""
|
"""Initialize the wrapper."""
|
||||||
self.device = None
|
|
||||||
self.message = None
|
self.message = None
|
||||||
self.event = asyncio.Event()
|
self.event = asyncio.Event()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def callback(self, device, message):
|
def callback(self, bulb, message):
|
||||||
"""Handle responses."""
|
"""Handle responses."""
|
||||||
self.device = device
|
|
||||||
self.message = message
|
self.message = message
|
||||||
self.event.set()
|
self.event.set()
|
||||||
|
|
||||||
async def wait(self, method):
|
async def wait(self, method):
|
||||||
"""Call an aiolifx method and wait for its response."""
|
"""Call an aiolifx method and wait for its response."""
|
||||||
self.device = None
|
|
||||||
self.message = None
|
self.message = None
|
||||||
self.event.clear()
|
self.event.clear()
|
||||||
method(callb=self.callback)
|
method(callb=self.callback)
|
||||||
@ -387,9 +384,9 @@ def convert_16_to_8(value):
|
|||||||
class LIFXLight(Light):
|
class LIFXLight(Light):
|
||||||
"""Representation of a LIFX light."""
|
"""Representation of a LIFX light."""
|
||||||
|
|
||||||
def __init__(self, device, effects_conductor):
|
def __init__(self, bulb, effects_conductor):
|
||||||
"""Initialize the light."""
|
"""Initialize the light."""
|
||||||
self.light = device
|
self.bulb = bulb
|
||||||
self.effects_conductor = effects_conductor
|
self.effects_conductor = effects_conductor
|
||||||
self.registered = True
|
self.registered = True
|
||||||
self.postponed_update = None
|
self.postponed_update = None
|
||||||
@ -397,34 +394,34 @@ class LIFXLight(Light):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self):
|
def available(self):
|
||||||
"""Return the availability of the device."""
|
"""Return the availability of the bulb."""
|
||||||
return self.registered
|
return self.registered
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
"""Return a unique ID."""
|
"""Return a unique ID."""
|
||||||
return self.light.mac_addr
|
return self.bulb.mac_addr
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the device."""
|
"""Return the name of the bulb."""
|
||||||
return self.light.label
|
return self.bulb.label
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def who(self):
|
def who(self):
|
||||||
"""Return a string identifying the device."""
|
"""Return a string identifying the bulb."""
|
||||||
return "%s (%s)" % (self.light.ip_addr, self.name)
|
return "%s (%s)" % (self.bulb.ip_addr, self.name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def min_mireds(self):
|
def min_mireds(self):
|
||||||
"""Return the coldest color_temp that this light supports."""
|
"""Return the coldest color_temp that this light supports."""
|
||||||
kelvin = lifx_features(self.light)['max_kelvin']
|
kelvin = lifx_features(self.bulb)['max_kelvin']
|
||||||
return math.floor(color_util.color_temperature_kelvin_to_mired(kelvin))
|
return math.floor(color_util.color_temperature_kelvin_to_mired(kelvin))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def max_mireds(self):
|
def max_mireds(self):
|
||||||
"""Return the warmest color_temp that this light supports."""
|
"""Return the warmest color_temp that this light supports."""
|
||||||
kelvin = lifx_features(self.light)['min_kelvin']
|
kelvin = lifx_features(self.bulb)['min_kelvin']
|
||||||
return math.ceil(color_util.color_temperature_kelvin_to_mired(kelvin))
|
return math.ceil(color_util.color_temperature_kelvin_to_mired(kelvin))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -432,8 +429,8 @@ class LIFXLight(Light):
|
|||||||
"""Flag supported features."""
|
"""Flag supported features."""
|
||||||
support = SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION | SUPPORT_EFFECT
|
support = SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION | SUPPORT_EFFECT
|
||||||
|
|
||||||
device_features = lifx_features(self.light)
|
bulb_features = lifx_features(self.bulb)
|
||||||
if device_features['min_kelvin'] != device_features['max_kelvin']:
|
if bulb_features['min_kelvin'] != bulb_features['max_kelvin']:
|
||||||
support |= SUPPORT_COLOR_TEMP
|
support |= SUPPORT_COLOR_TEMP
|
||||||
|
|
||||||
return support
|
return support
|
||||||
@ -441,25 +438,25 @@ class LIFXLight(Light):
|
|||||||
@property
|
@property
|
||||||
def brightness(self):
|
def brightness(self):
|
||||||
"""Return the brightness of this light between 0..255."""
|
"""Return the brightness of this light between 0..255."""
|
||||||
return convert_16_to_8(self.light.color[2])
|
return convert_16_to_8(self.bulb.color[2])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def color_temp(self):
|
def color_temp(self):
|
||||||
"""Return the color temperature."""
|
"""Return the color temperature."""
|
||||||
_, sat, _, kelvin = self.light.color
|
_, sat, _, kelvin = self.bulb.color
|
||||||
if sat:
|
if sat:
|
||||||
return None
|
return None
|
||||||
return color_util.color_temperature_kelvin_to_mired(kelvin)
|
return color_util.color_temperature_kelvin_to_mired(kelvin)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""Return true if device is on."""
|
"""Return true if light is on."""
|
||||||
return self.light.power_level != 0
|
return self.bulb.power_level != 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def effect(self):
|
def effect(self):
|
||||||
"""Return the name of the currently running effect."""
|
"""Return the name of the currently running effect."""
|
||||||
effect = self.effects_conductor.effect(self.light)
|
effect = self.effects_conductor.effect(self.bulb)
|
||||||
if effect:
|
if effect:
|
||||||
return 'lifx_effect_' + effect.name
|
return 'lifx_effect_' + effect.name
|
||||||
return None
|
return None
|
||||||
@ -485,19 +482,19 @@ class LIFXLight(Light):
|
|||||||
util.dt.utcnow() + timedelta(milliseconds=when))
|
util.dt.utcnow() + timedelta(milliseconds=when))
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs):
|
async def async_turn_on(self, **kwargs):
|
||||||
"""Turn the device on."""
|
"""Turn the light on."""
|
||||||
kwargs[ATTR_POWER] = True
|
kwargs[ATTR_POWER] = True
|
||||||
self.hass.async_add_job(self.set_state(**kwargs))
|
self.hass.async_add_job(self.set_state(**kwargs))
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs):
|
async def async_turn_off(self, **kwargs):
|
||||||
"""Turn the device off."""
|
"""Turn the light off."""
|
||||||
kwargs[ATTR_POWER] = False
|
kwargs[ATTR_POWER] = False
|
||||||
self.hass.async_add_job(self.set_state(**kwargs))
|
self.hass.async_add_job(self.set_state(**kwargs))
|
||||||
|
|
||||||
async def set_state(self, **kwargs):
|
async def set_state(self, **kwargs):
|
||||||
"""Set a color on the light and turn it on/off."""
|
"""Set a color on the light and turn it on/off."""
|
||||||
async with self.lock:
|
async with self.lock:
|
||||||
bulb = self.light
|
bulb = self.bulb
|
||||||
|
|
||||||
await self.effects_conductor.stop([bulb])
|
await self.effects_conductor.stop([bulb])
|
||||||
|
|
||||||
@ -544,13 +541,13 @@ class LIFXLight(Light):
|
|||||||
await self.update_during_transition(fade)
|
await self.update_during_transition(fade)
|
||||||
|
|
||||||
async def set_power(self, ack, pwr, duration=0):
|
async def set_power(self, ack, pwr, duration=0):
|
||||||
"""Send a power change to the device."""
|
"""Send a power change to the bulb."""
|
||||||
await ack(partial(self.light.set_power, pwr, duration=duration))
|
await ack(partial(self.bulb.set_power, pwr, duration=duration))
|
||||||
|
|
||||||
async def set_color(self, ack, hsbk, kwargs, duration=0):
|
async def set_color(self, ack, hsbk, kwargs, duration=0):
|
||||||
"""Send a color change to the device."""
|
"""Send a color change to the bulb."""
|
||||||
hsbk = merge_hsbk(self.light.color, hsbk)
|
hsbk = merge_hsbk(self.bulb.color, hsbk)
|
||||||
await ack(partial(self.light.set_color, hsbk, duration=duration))
|
await ack(partial(self.bulb.set_color, hsbk, duration=duration))
|
||||||
|
|
||||||
async def default_effect(self, **kwargs):
|
async def default_effect(self, **kwargs):
|
||||||
"""Start an effect with default parameters."""
|
"""Start an effect with default parameters."""
|
||||||
@ -563,7 +560,7 @@ class LIFXLight(Light):
|
|||||||
async def async_update(self):
|
async def async_update(self):
|
||||||
"""Update bulb status."""
|
"""Update bulb status."""
|
||||||
if self.available and not self.lock.locked():
|
if self.available and not self.lock.locked():
|
||||||
await AwaitAioLIFX().wait(self.light.get_color)
|
await AwaitAioLIFX().wait(self.bulb.get_color)
|
||||||
|
|
||||||
|
|
||||||
class LIFXWhite(LIFXLight):
|
class LIFXWhite(LIFXLight):
|
||||||
@ -600,7 +597,7 @@ class LIFXColor(LIFXLight):
|
|||||||
@property
|
@property
|
||||||
def hs_color(self):
|
def hs_color(self):
|
||||||
"""Return the hs value."""
|
"""Return the hs value."""
|
||||||
hue, sat, _, _ = self.light.color
|
hue, sat, _, _ = self.bulb.color
|
||||||
hue = hue / 65535 * 360
|
hue = hue / 65535 * 360
|
||||||
sat = sat / 65535 * 100
|
sat = sat / 65535 * 100
|
||||||
return (hue, sat) if sat else None
|
return (hue, sat) if sat else None
|
||||||
@ -610,8 +607,8 @@ class LIFXStrip(LIFXColor):
|
|||||||
"""Representation of a LIFX light strip with multiple zones."""
|
"""Representation of a LIFX light strip with multiple zones."""
|
||||||
|
|
||||||
async def set_color(self, ack, hsbk, kwargs, duration=0):
|
async def set_color(self, ack, hsbk, kwargs, duration=0):
|
||||||
"""Send a color change to the device."""
|
"""Send a color change to the bulb."""
|
||||||
bulb = self.light
|
bulb = self.bulb
|
||||||
num_zones = len(bulb.color_zones)
|
num_zones = len(bulb.color_zones)
|
||||||
|
|
||||||
zones = kwargs.get(ATTR_ZONES)
|
zones = kwargs.get(ATTR_ZONES)
|
||||||
@ -659,7 +656,7 @@ class LIFXStrip(LIFXColor):
|
|||||||
while self.available and zone < top:
|
while self.available and zone < top:
|
||||||
# Each get_color_zones can update 8 zones at once
|
# Each get_color_zones can update 8 zones at once
|
||||||
resp = await AwaitAioLIFX().wait(partial(
|
resp = await AwaitAioLIFX().wait(partial(
|
||||||
self.light.get_color_zones,
|
self.bulb.get_color_zones,
|
||||||
start_index=zone))
|
start_index=zone))
|
||||||
if resp:
|
if resp:
|
||||||
zone += 8
|
zone += 8
|
||||||
|
@ -17,7 +17,7 @@ from homeassistant.const import (
|
|||||||
from homeassistant.exceptions import PlatformNotReady
|
from homeassistant.exceptions import PlatformNotReady
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
REQUIREMENTS = ['python-songpal==0.0.7']
|
REQUIREMENTS = ['python-songpal==0.0.8']
|
||||||
|
|
||||||
SUPPORT_SONGPAL = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_STEP | \
|
SUPPORT_SONGPAL = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_STEP | \
|
||||||
SUPPORT_VOLUME_MUTE | SUPPORT_SELECT_SOURCE | \
|
SUPPORT_VOLUME_MUTE | SUPPORT_SELECT_SOURCE | \
|
||||||
|
7
homeassistant/components/nest/.translations/fr.json
Normal file
7
homeassistant/components/nest/.translations/fr.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"already_setup": "Vous ne pouvez configurer qu'un seul compte Nest."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -169,7 +169,7 @@ def _add_columns(engine, table_name, columns_def):
|
|||||||
if 'duplicate' not in str(err).lower():
|
if 'duplicate' not in str(err).lower():
|
||||||
raise
|
raise
|
||||||
|
|
||||||
_LOGGER.warning('Column %s already exists on %s, continueing',
|
_LOGGER.warning('Column %s already exists on %s, continuing',
|
||||||
column_def.split(' ')[1], table_name)
|
column_def.split(' ')[1], table_name)
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,6 +38,10 @@ class DeconzScene(Scene):
|
|||||||
"""Subscribe to sensors events."""
|
"""Subscribe to sensors events."""
|
||||||
self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._scene.deconz_id
|
self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._scene.deconz_id
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
|
"""Disconnect scene object when removed."""
|
||||||
|
self._scene = None
|
||||||
|
|
||||||
async def async_activate(self):
|
async def async_activate(self):
|
||||||
"""Activate the scene."""
|
"""Activate the scene."""
|
||||||
await self._scene.async_set_state({})
|
await self._scene.async_set_state({})
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
{
|
{
|
||||||
"state": {
|
"state": {
|
||||||
"first_quarter": "Quarto crescente",
|
|
||||||
"full_moon": "Cheia",
|
"full_moon": "Cheia",
|
||||||
"last_quarter": "Quarto minguante",
|
|
||||||
"new_moon": "Nova",
|
"new_moon": "Nova",
|
||||||
"waning_crescent": "Minguante",
|
"waning_crescent": "Minguante",
|
||||||
"waning_gibbous": "Minguante gibosa",
|
"waning_gibbous": "Minguante gibosa",
|
||||||
|
12
homeassistant/components/sensor/.translations/moon.pt.json
Normal file
12
homeassistant/components/sensor/.translations/moon.pt.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"state": {
|
||||||
|
"first_quarter": "Quarto crescente",
|
||||||
|
"full_moon": "Lua cheia",
|
||||||
|
"last_quarter": "Quarto minguante",
|
||||||
|
"new_moon": "Lua nova",
|
||||||
|
"waning_crescent": "Lua crescente",
|
||||||
|
"waning_gibbous": "Minguante convexa",
|
||||||
|
"waxing_crescent": "Lua minguante",
|
||||||
|
"waxing_gibbous": "Crescente convexa"
|
||||||
|
}
|
||||||
|
}
|
@ -64,6 +64,11 @@ class DeconzSensor(Entity):
|
|||||||
self._sensor.register_async_callback(self.async_update_callback)
|
self._sensor.register_async_callback(self.async_update_callback)
|
||||||
self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._sensor.deconz_id
|
self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._sensor.deconz_id
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
|
"""Disconnect sensor object when removed."""
|
||||||
|
self._sensor.remove_callback(self.async_update_callback)
|
||||||
|
self._sensor = None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self, reason):
|
def async_update_callback(self, reason):
|
||||||
"""Update the sensor's state.
|
"""Update the sensor's state.
|
||||||
@ -155,16 +160,21 @@ class DeconzSensor(Entity):
|
|||||||
class DeconzBattery(Entity):
|
class DeconzBattery(Entity):
|
||||||
"""Battery class for when a device is only represented as an event."""
|
"""Battery class for when a device is only represented as an event."""
|
||||||
|
|
||||||
def __init__(self, device):
|
def __init__(self, sensor):
|
||||||
"""Register dispatcher callback for update of battery state."""
|
"""Register dispatcher callback for update of battery state."""
|
||||||
self._device = device
|
self._sensor = sensor
|
||||||
self._name = '{} {}'.format(self._device.name, 'Battery Level')
|
self._name = '{} {}'.format(self._sensor.name, 'Battery Level')
|
||||||
self._unit_of_measurement = "%"
|
self._unit_of_measurement = "%"
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Subscribe to sensors events."""
|
"""Subscribe to sensors events."""
|
||||||
self._device.register_async_callback(self.async_update_callback)
|
self._sensor.register_async_callback(self.async_update_callback)
|
||||||
self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._device.deconz_id
|
self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._sensor.deconz_id
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
|
"""Disconnect sensor object when removed."""
|
||||||
|
self._sensor.remove_callback(self.async_update_callback)
|
||||||
|
self._sensor = None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self, reason):
|
def async_update_callback(self, reason):
|
||||||
@ -175,7 +185,7 @@ class DeconzBattery(Entity):
|
|||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Return the state of the battery."""
|
"""Return the state of the battery."""
|
||||||
return self._device.battery
|
return self._sensor.battery
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -185,7 +195,7 @@ class DeconzBattery(Entity):
|
|||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
"""Return a unique identifier for the device."""
|
"""Return a unique identifier for the device."""
|
||||||
return self._device.uniqueid
|
return self._sensor.uniqueid
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_class(self):
|
def device_class(self):
|
||||||
@ -206,22 +216,22 @@ class DeconzBattery(Entity):
|
|||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
"""Return the state attributes of the battery."""
|
"""Return the state attributes of the battery."""
|
||||||
attr = {
|
attr = {
|
||||||
ATTR_EVENT_ID: slugify(self._device.name),
|
ATTR_EVENT_ID: slugify(self._sensor.name),
|
||||||
}
|
}
|
||||||
return attr
|
return attr
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_info(self):
|
def device_info(self):
|
||||||
"""Return a device description for device registry."""
|
"""Return a device description for device registry."""
|
||||||
if (self._device.uniqueid is None or
|
if (self._sensor.uniqueid is None or
|
||||||
self._device.uniqueid.count(':') != 7):
|
self._sensor.uniqueid.count(':') != 7):
|
||||||
return None
|
return None
|
||||||
serial = self._device.uniqueid.split('-', 1)[0]
|
serial = self._sensor.uniqueid.split('-', 1)[0]
|
||||||
return {
|
return {
|
||||||
'connections': {(CONNECTION_ZIGBEE, serial)},
|
'connections': {(CONNECTION_ZIGBEE, serial)},
|
||||||
'identifiers': {(DECONZ_DOMAIN, serial)},
|
'identifiers': {(DECONZ_DOMAIN, serial)},
|
||||||
'manufacturer': self._device.manufacturer,
|
'manufacturer': self._sensor.manufacturer,
|
||||||
'model': self._device.modelid,
|
'model': self._sensor.modelid,
|
||||||
'name': self._device.name,
|
'name': self._sensor.name,
|
||||||
'sw_version': self._device.swversion,
|
'sw_version': self._sensor.swversion,
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ from homeassistant.helpers.entity import Entity
|
|||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
from homeassistant.util.temperature import celsius_to_fahrenheit
|
from homeassistant.util.temperature import celsius_to_fahrenheit
|
||||||
|
|
||||||
REQUIREMENTS = ['Adafruit-DHT==1.3.3']
|
REQUIREMENTS = ['Adafruit-DHT==1.3.4']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
},
|
},
|
||||||
"step": {
|
"step": {
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"description": "M\u00f6chten Sie Sonos konfigurieren?",
|
"description": "M\u00f6chten Sie Sonos einrichten?",
|
||||||
"title": "Sonos"
|
"title": "Sonos"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -55,6 +55,11 @@ class DeconzSwitch(SwitchDevice):
|
|||||||
self._switch.register_async_callback(self.async_update_callback)
|
self._switch.register_async_callback(self.async_update_callback)
|
||||||
self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._switch.deconz_id
|
self.hass.data[DATA_DECONZ_ID][self.entity_id] = self._switch.deconz_id
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
|
"""Disconnect switch object when removed."""
|
||||||
|
self._switch.remove_callback(self.async_update_callback)
|
||||||
|
self._switch = None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self, reason):
|
def async_update_callback(self, reason):
|
||||||
"""Update the switch's state."""
|
"""Update the switch's state."""
|
||||||
|
@ -87,6 +87,7 @@ SUPPORT_XIAOMI = SUPPORT_STATE | SUPPORT_PAUSE | \
|
|||||||
|
|
||||||
|
|
||||||
STATE_CODE_TO_STATE = {
|
STATE_CODE_TO_STATE = {
|
||||||
|
2: STATE_IDLE,
|
||||||
3: STATE_IDLE,
|
3: STATE_IDLE,
|
||||||
5: STATE_CLEANING,
|
5: STATE_CLEANING,
|
||||||
6: STATE_RETURNING,
|
6: STATE_RETURNING,
|
||||||
|
@ -124,14 +124,14 @@ def setup(hass, config):
|
|||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
'Unable to get description url for %s',
|
'Unable to get description url for %s',
|
||||||
'{}:{}'.format(host, port) if port else host)
|
'{}:{}'.format(host, port) if port else host)
|
||||||
return False
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
device = pywemo.discovery.device_from_description(url, None)
|
device = pywemo.discovery.device_from_description(url, None)
|
||||||
except (requests.exceptions.ConnectionError,
|
except (requests.exceptions.ConnectionError,
|
||||||
requests.exceptions.Timeout) as err:
|
requests.exceptions.Timeout) as err:
|
||||||
_LOGGER.error('Unable to access %s (%s)', url, err)
|
_LOGGER.error('Unable to access %s (%s)', url, err)
|
||||||
return False
|
continue
|
||||||
|
|
||||||
devices.append((url, device))
|
devices.append((url, device))
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ voluptuous==0.11.5
|
|||||||
--only-binary=all nuimo==0.1.0
|
--only-binary=all nuimo==0.1.0
|
||||||
|
|
||||||
# homeassistant.components.sensor.dht
|
# homeassistant.components.sensor.dht
|
||||||
# Adafruit-DHT==1.3.3
|
# Adafruit-DHT==1.3.4
|
||||||
|
|
||||||
# homeassistant.components.sensor.sht31
|
# homeassistant.components.sensor.sht31
|
||||||
Adafruit-GPIO==1.0.3
|
Adafruit-GPIO==1.0.3
|
||||||
@ -139,7 +139,7 @@ apcaccess==0.0.13
|
|||||||
apns2==0.3.0
|
apns2==0.3.0
|
||||||
|
|
||||||
# homeassistant.components.asterisk_mbox
|
# homeassistant.components.asterisk_mbox
|
||||||
asterisk_mbox==0.4.0
|
asterisk_mbox==0.5.0
|
||||||
|
|
||||||
# homeassistant.components.igd
|
# homeassistant.components.igd
|
||||||
# homeassistant.components.media_player.dlna_dmr
|
# homeassistant.components.media_player.dlna_dmr
|
||||||
@ -449,7 +449,7 @@ hole==0.3.0
|
|||||||
holidays==0.9.6
|
holidays==0.9.6
|
||||||
|
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20180829.0
|
home-assistant-frontend==20180831.0
|
||||||
|
|
||||||
# homeassistant.components.homekit_controller
|
# homeassistant.components.homekit_controller
|
||||||
# homekit==0.10
|
# homekit==0.10
|
||||||
@ -483,7 +483,7 @@ ihcsdk==2.2.0
|
|||||||
influxdb==5.0.0
|
influxdb==5.0.0
|
||||||
|
|
||||||
# homeassistant.components.insteon
|
# homeassistant.components.insteon
|
||||||
insteonplm==0.12.3
|
insteonplm==0.13.1
|
||||||
|
|
||||||
# homeassistant.components.sensor.iperf3
|
# homeassistant.components.sensor.iperf3
|
||||||
iperf3==0.1.10
|
iperf3==0.1.10
|
||||||
@ -821,7 +821,7 @@ pycsspeechtts==1.0.2
|
|||||||
pydaikin==0.4
|
pydaikin==0.4
|
||||||
|
|
||||||
# homeassistant.components.deconz
|
# homeassistant.components.deconz
|
||||||
pydeconz==44
|
pydeconz==45
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
# homeassistant.components.zwave
|
||||||
pydispatcher==2.0.5
|
pydispatcher==2.0.5
|
||||||
@ -1131,7 +1131,7 @@ python-roku==3.1.5
|
|||||||
python-sochain-api==0.0.2
|
python-sochain-api==0.0.2
|
||||||
|
|
||||||
# homeassistant.components.media_player.songpal
|
# homeassistant.components.media_player.songpal
|
||||||
python-songpal==0.0.7
|
python-songpal==0.0.8
|
||||||
|
|
||||||
# homeassistant.components.sensor.synologydsm
|
# homeassistant.components.sensor.synologydsm
|
||||||
python-synology==0.2.0
|
python-synology==0.2.0
|
||||||
|
@ -84,7 +84,7 @@ hbmqtt==0.9.2
|
|||||||
holidays==0.9.6
|
holidays==0.9.6
|
||||||
|
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20180829.0
|
home-assistant-frontend==20180831.0
|
||||||
|
|
||||||
# homeassistant.components.homematicip_cloud
|
# homeassistant.components.homematicip_cloud
|
||||||
homematicip==0.9.8
|
homematicip==0.9.8
|
||||||
@ -139,7 +139,7 @@ py-canary==0.5.0
|
|||||||
pyblackbird==0.5
|
pyblackbird==0.5
|
||||||
|
|
||||||
# homeassistant.components.deconz
|
# homeassistant.components.deconz
|
||||||
pydeconz==44
|
pydeconz==45
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
# homeassistant.components.zwave
|
||||||
pydispatcher==2.0.5
|
pydispatcher==2.0.5
|
||||||
|
1
tests/components/geo_location/__init__.py
Normal file
1
tests/components/geo_location/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""The tests for Geo Location platforms."""
|
63
tests/components/geo_location/test_demo.py
Normal file
63
tests/components/geo_location/test_demo.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
"""The tests for the demo platform."""
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from homeassistant.components import geo_location
|
||||||
|
from homeassistant.components.geo_location.demo import \
|
||||||
|
NUMBER_OF_DEMO_DEVICES, DEFAULT_UNIT_OF_MEASUREMENT, \
|
||||||
|
DEFAULT_UPDATE_INTERVAL
|
||||||
|
from homeassistant.setup import setup_component
|
||||||
|
from tests.common import get_test_home_assistant, assert_setup_component, \
|
||||||
|
fire_time_changed
|
||||||
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
|
CONFIG = {
|
||||||
|
geo_location.DOMAIN: [
|
||||||
|
{
|
||||||
|
'platform': 'demo'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestDemoPlatform(unittest.TestCase):
|
||||||
|
"""Test the demo platform."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Initialize values for this testcase class."""
|
||||||
|
self.hass = get_test_home_assistant()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""Stop everything that was started."""
|
||||||
|
self.hass.stop()
|
||||||
|
|
||||||
|
def test_setup_platform(self):
|
||||||
|
"""Test setup of demo platform via configuration."""
|
||||||
|
utcnow = dt_util.utcnow()
|
||||||
|
# Patching 'utcnow' to gain more control over the timed update.
|
||||||
|
with patch('homeassistant.util.dt.utcnow', return_value=utcnow):
|
||||||
|
with assert_setup_component(1, geo_location.DOMAIN):
|
||||||
|
self.assertTrue(setup_component(self.hass, geo_location.DOMAIN,
|
||||||
|
CONFIG))
|
||||||
|
|
||||||
|
# In this test, only entities of the geo location domain have been
|
||||||
|
# generated.
|
||||||
|
all_states = self.hass.states.all()
|
||||||
|
assert len(all_states) == NUMBER_OF_DEMO_DEVICES
|
||||||
|
|
||||||
|
# Check a single device's attributes.
|
||||||
|
state_first_entry = all_states[0]
|
||||||
|
self.assertAlmostEqual(state_first_entry.attributes['latitude'],
|
||||||
|
self.hass.config.latitude, delta=1.0)
|
||||||
|
self.assertAlmostEqual(state_first_entry.attributes['longitude'],
|
||||||
|
self.hass.config.longitude, delta=1.0)
|
||||||
|
assert state_first_entry.attributes['unit_of_measurement'] == \
|
||||||
|
DEFAULT_UNIT_OF_MEASUREMENT
|
||||||
|
# Update (replaces 1 device).
|
||||||
|
fire_time_changed(self.hass, utcnow + DEFAULT_UPDATE_INTERVAL)
|
||||||
|
self.hass.block_till_done()
|
||||||
|
# Get all states again, ensure that the number of states is still
|
||||||
|
# the same, but the lists are different.
|
||||||
|
all_states_updated = self.hass.states.all()
|
||||||
|
assert len(all_states_updated) == NUMBER_OF_DEMO_DEVICES
|
||||||
|
self.assertNotEqual(all_states, all_states_updated)
|
20
tests/components/geo_location/test_init.py
Normal file
20
tests/components/geo_location/test_init.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
"""The tests for the geo location component."""
|
||||||
|
from homeassistant.components import geo_location
|
||||||
|
from homeassistant.components.geo_location import GeoLocationEvent
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
|
||||||
|
async def test_setup_component(hass):
|
||||||
|
"""Simple test setup of component."""
|
||||||
|
result = await async_setup_component(hass, geo_location.DOMAIN)
|
||||||
|
assert result
|
||||||
|
|
||||||
|
|
||||||
|
async def test_event(hass):
|
||||||
|
"""Simple test of the geo location event class."""
|
||||||
|
entity = GeoLocationEvent()
|
||||||
|
|
||||||
|
assert entity.state is None
|
||||||
|
assert entity.distance is None
|
||||||
|
assert entity.latitude is None
|
||||||
|
assert entity.longitude is None
|
@ -160,11 +160,11 @@ class TestFeedreaderComponent(unittest.TestCase):
|
|||||||
manager, events = self.setup_manager(feed_data, max_entries=5)
|
manager, events = self.setup_manager(feed_data, max_entries=5)
|
||||||
assert len(events) == 5
|
assert len(events) == 5
|
||||||
|
|
||||||
def test_feed_without_publication_date(self):
|
def test_feed_without_publication_date_and_title(self):
|
||||||
"""Test simple feed with entry without publication date."""
|
"""Test simple feed with entry without publication date and title."""
|
||||||
feed_data = load_fixture('feedreader3.xml')
|
feed_data = load_fixture('feedreader3.xml')
|
||||||
manager, events = self.setup_manager(feed_data)
|
manager, events = self.setup_manager(feed_data)
|
||||||
assert len(events) == 2
|
assert len(events) == 3
|
||||||
|
|
||||||
def test_feed_invalid_data(self):
|
def test_feed_invalid_data(self):
|
||||||
"""Test feed with invalid data."""
|
"""Test feed with invalid data."""
|
||||||
|
5
tests/fixtures/feedreader3.xml
vendored
5
tests/fixtures/feedreader3.xml
vendored
@ -21,6 +21,11 @@
|
|||||||
<link>http://www.example.com/link/2</link>
|
<link>http://www.example.com/link/2</link>
|
||||||
<guid isPermaLink="false">GUID 2</guid>
|
<guid isPermaLink="false">GUID 2</guid>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<description>Description 3</description>
|
||||||
|
<link>http://www.example.com/link/3</link>
|
||||||
|
<guid isPermaLink="false">GUID 3</guid>
|
||||||
|
</item>
|
||||||
|
|
||||||
</channel>
|
</channel>
|
||||||
</rss>
|
</rss>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user