mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 11:47:06 +00:00
Merge pull request #41406 from home-assistant/rc
This commit is contained in:
commit
55958bcfb7
38
.coveragerc
38
.coveragerc
@ -33,7 +33,11 @@ omit =
|
||||
homeassistant/components/airvisual/air_quality.py
|
||||
homeassistant/components/airvisual/sensor.py
|
||||
homeassistant/components/aladdin_connect/cover.py
|
||||
homeassistant/components/alarmdecoder/*
|
||||
homeassistant/components/alarmdecoder/__init__.py
|
||||
homeassistant/components/alarmdecoder/alarm_control_panel.py
|
||||
homeassistant/components/alarmdecoder/binary_sensor.py
|
||||
homeassistant/components/alarmdecoder/const.py
|
||||
homeassistant/components/alarmdecoder/sensor.py
|
||||
homeassistant/components/alpha_vantage/sensor.py
|
||||
homeassistant/components/amazon_polly/tts.py
|
||||
homeassistant/components/ambiclimate/climate.py
|
||||
@ -117,7 +121,6 @@ omit =
|
||||
homeassistant/components/buienradar/util.py
|
||||
homeassistant/components/buienradar/weather.py
|
||||
homeassistant/components/caldav/calendar.py
|
||||
homeassistant/components/canary/alarm_control_panel.py
|
||||
homeassistant/components/canary/camera.py
|
||||
homeassistant/components/cast/*
|
||||
homeassistant/components/cert_expiry/helper.py
|
||||
@ -266,7 +269,9 @@ omit =
|
||||
homeassistant/components/firmata/board.py
|
||||
homeassistant/components/firmata/const.py
|
||||
homeassistant/components/firmata/entity.py
|
||||
homeassistant/components/firmata/light.py
|
||||
homeassistant/components/firmata/pin.py
|
||||
homeassistant/components/firmata/sensor.py
|
||||
homeassistant/components/firmata/switch.py
|
||||
homeassistant/components/fitbit/sensor.py
|
||||
homeassistant/components/fixer/sensor.py
|
||||
@ -315,6 +320,8 @@ omit =
|
||||
homeassistant/components/glances/sensor.py
|
||||
homeassistant/components/gntp/notify.py
|
||||
homeassistant/components/goalfeed/*
|
||||
homeassistant/components/goalzero/__init__.py
|
||||
homeassistant/components/goalzero/binary_sensor.py
|
||||
homeassistant/components/google/*
|
||||
homeassistant/components/google_cloud/tts.py
|
||||
homeassistant/components/google_maps/device_tracker.py
|
||||
@ -369,6 +376,7 @@ omit =
|
||||
homeassistant/components/hunterdouglas_powerview/sensor.py
|
||||
homeassistant/components/hunterdouglas_powerview/cover.py
|
||||
homeassistant/components/hunterdouglas_powerview/entity.py
|
||||
homeassistant/components/hvv_departures/binary_sensor.py
|
||||
homeassistant/components/hvv_departures/sensor.py
|
||||
homeassistant/components/hvv_departures/__init__.py
|
||||
homeassistant/components/hydrawise/*
|
||||
@ -478,7 +486,8 @@ omit =
|
||||
homeassistant/components/london_underground/sensor.py
|
||||
homeassistant/components/loopenergy/sensor.py
|
||||
homeassistant/components/luci/device_tracker.py
|
||||
homeassistant/components/luftdaten/*
|
||||
homeassistant/components/luftdaten/__init__.py
|
||||
homeassistant/components/luftdaten/sensor.py
|
||||
homeassistant/components/lupusec/*
|
||||
homeassistant/components/lutron/*
|
||||
homeassistant/components/lutron_caseta/__init__.py
|
||||
@ -530,7 +539,9 @@ omit =
|
||||
homeassistant/components/mjpeg/camera.py
|
||||
homeassistant/components/mobile_app/*
|
||||
homeassistant/components/mochad/*
|
||||
homeassistant/components/modbus/*
|
||||
homeassistant/components/modbus/climate.py
|
||||
homeassistant/components/modbus/cover.py
|
||||
homeassistant/components/modbus/switch.py
|
||||
homeassistant/components/modem_callerid/sensor.py
|
||||
homeassistant/components/mpchc/media_player.py
|
||||
homeassistant/components/mpd/media_player.py
|
||||
@ -595,6 +606,10 @@ omit =
|
||||
homeassistant/components/oasa_telematics/sensor.py
|
||||
homeassistant/components/ohmconnect/sensor.py
|
||||
homeassistant/components/ombi/*
|
||||
homeassistant/components/omnilogic/__init__.py
|
||||
homeassistant/components/omnilogic/common.py
|
||||
homeassistant/components/omnilogic/sensor.py
|
||||
homeassistant/components/onewire/const.py
|
||||
homeassistant/components/onewire/sensor.py
|
||||
homeassistant/components/onkyo/media_player.py
|
||||
homeassistant/components/onvif/__init__.py
|
||||
@ -803,6 +818,7 @@ omit =
|
||||
homeassistant/components/spc/*
|
||||
homeassistant/components/speedtestdotnet/*
|
||||
homeassistant/components/spider/*
|
||||
homeassistant/components/splunk/*
|
||||
homeassistant/components/spotcrime/sensor.py
|
||||
homeassistant/components/spotify/__init__.py
|
||||
homeassistant/components/spotify/media_player.py
|
||||
@ -830,7 +846,9 @@ omit =
|
||||
homeassistant/components/synology_chat/notify.py
|
||||
homeassistant/components/synology_dsm/__init__.py
|
||||
homeassistant/components/synology_dsm/binary_sensor.py
|
||||
homeassistant/components/synology_dsm/camera.py
|
||||
homeassistant/components/synology_dsm/sensor.py
|
||||
homeassistant/components/synology_dsm/switch.py
|
||||
homeassistant/components/synology_srm/device_tracker.py
|
||||
homeassistant/components/syslog/notify.py
|
||||
homeassistant/components/systemmonitor/sensor.py
|
||||
@ -844,7 +862,13 @@ omit =
|
||||
homeassistant/components/ted5000/sensor.py
|
||||
homeassistant/components/telegram/notify.py
|
||||
homeassistant/components/telegram_bot/*
|
||||
homeassistant/components/tellduslive/*
|
||||
homeassistant/components/tellduslive/__init__.py
|
||||
homeassistant/components/tellduslive/binary_sensor.py
|
||||
homeassistant/components/tellduslive/cover.py
|
||||
homeassistant/components/tellduslive/entry.py
|
||||
homeassistant/components/tellduslive/light.py
|
||||
homeassistant/components/tellduslive/sensor.py
|
||||
homeassistant/components/tellduslive/switch.py
|
||||
homeassistant/components/tellstick/*
|
||||
homeassistant/components/telnet/switch.py
|
||||
homeassistant/components/temper/sensor.py
|
||||
@ -863,7 +887,9 @@ omit =
|
||||
homeassistant/components/thingspeak/*
|
||||
homeassistant/components/thinkingcleaner/*
|
||||
homeassistant/components/thomson/device_tracker.py
|
||||
homeassistant/components/tibber/*
|
||||
homeassistant/components/tibber/__init__.py
|
||||
homeassistant/components/tibber/notify.py
|
||||
homeassistant/components/tibber/sensor.py
|
||||
homeassistant/components/tikteck/light.py
|
||||
homeassistant/components/tile/__init__.py
|
||||
homeassistant/components/tile/device_tracker.py
|
||||
|
23
.github/workflows/ci.yaml
vendored
23
.github/workflows/ci.yaml
vendored
@ -518,25 +518,24 @@ jobs:
|
||||
hassfest:
|
||||
name: Check hassfest
|
||||
runs-on: ubuntu-latest
|
||||
needs: prepare-base
|
||||
needs: prepare-tests
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.7]
|
||||
container: homeassistant/ci-azure:${{ matrix.python-version }}
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.1.2
|
||||
id: python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Restore base Python virtual environment
|
||||
- name:
|
||||
Restore full Python ${{ matrix.python-version }} virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: venv
|
||||
key: >-
|
||||
${{ env.CACHE_VERSION}}-${{ runner.os }}-base-venv-${{
|
||||
steps.python.outputs.python-version }}-${{
|
||||
hashFiles('requirements.txt') }}-${{
|
||||
hashFiles('requirements_test.txt') }}-${{
|
||||
${{ env.CACHE_VERSION}}-${{ runner.os }}-venv-${{
|
||||
matrix.python-version }}-${{ hashFiles('requirements_test.txt')
|
||||
}}-${{ hashFiles('requirements_all.txt') }}-${{
|
||||
hashFiles('homeassistant/package_constraints.txt') }}
|
||||
- name: Fail job if Python cache restore failed
|
||||
if: steps.cache-venv.outputs.cache-hit != 'true'
|
||||
@ -546,7 +545,7 @@ jobs:
|
||||
- name: Run hassfest
|
||||
run: |
|
||||
. venv/bin/activate
|
||||
python -m script.hassfest --action validate
|
||||
python -m script.hassfest --requirements --action validate
|
||||
|
||||
gen-requirements-all:
|
||||
name: Check all requirements
|
||||
|
@ -39,7 +39,7 @@ repos:
|
||||
- --configfile=tests/bandit.yaml
|
||||
files: ^(homeassistant|script|tests)/.+\.py$
|
||||
- repo: https://github.com/PyCQA/isort
|
||||
rev: 5.5.1
|
||||
rev: 5.5.3
|
||||
hooks:
|
||||
- id: isort
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
|
17
CODEOWNERS
17
CODEOWNERS
@ -157,6 +157,7 @@ homeassistant/components/geonetnz_volcano/* @exxamalte
|
||||
homeassistant/components/gios/* @bieniu
|
||||
homeassistant/components/gitter/* @fabaff
|
||||
homeassistant/components/glances/* @fabaff @engrbm87
|
||||
homeassistant/components/goalzero/* @tkdrob
|
||||
homeassistant/components/gogogate2/* @vangorra
|
||||
homeassistant/components/google_assistant/* @home-assistant/cloud
|
||||
homeassistant/components/google_cloud/* @lufton
|
||||
@ -169,6 +170,7 @@ homeassistant/components/growatt_server/* @indykoning
|
||||
homeassistant/components/guardian/* @bachya
|
||||
homeassistant/components/harmony/* @ehendrix23 @bramkragten @bdraco
|
||||
homeassistant/components/hassio/* @home-assistant/hass-io
|
||||
homeassistant/components/hdmi_cec/* @newAM
|
||||
homeassistant/components/heatmiser/* @andylockran
|
||||
homeassistant/components/heos/* @andrewsayre
|
||||
homeassistant/components/here_travel_time/* @eifinger
|
||||
@ -193,6 +195,7 @@ homeassistant/components/humidifier/* @home-assistant/core @Shulyaka
|
||||
homeassistant/components/hunterdouglas_powerview/* @bdraco
|
||||
homeassistant/components/hvv_departures/* @vigonotion
|
||||
homeassistant/components/hydrawise/* @ptcryan
|
||||
homeassistant/components/hyperion/* @dermotduffy
|
||||
homeassistant/components/iammeter/* @lewei50
|
||||
homeassistant/components/iaqualink/* @flz
|
||||
homeassistant/components/icloud/* @Quentame
|
||||
@ -226,7 +229,7 @@ homeassistant/components/keenetic_ndms2/* @foxel
|
||||
homeassistant/components/kef/* @basnijholt
|
||||
homeassistant/components/keyboard_remote/* @bendavid
|
||||
homeassistant/components/knx/* @Julius2342 @farmio @marvin-w
|
||||
homeassistant/components/kodi/* @OnFreund
|
||||
homeassistant/components/kodi/* @OnFreund @cgtobi
|
||||
homeassistant/components/konnected/* @heythisisnate @kit-klein
|
||||
homeassistant/components/lametric/* @robbiet480
|
||||
homeassistant/components/launch_library/* @ludeeus
|
||||
@ -238,7 +241,7 @@ homeassistant/components/logger/* @home-assistant/core
|
||||
homeassistant/components/logi_circle/* @evanjd
|
||||
homeassistant/components/loopenergy/* @pavoni
|
||||
homeassistant/components/lovelace/* @home-assistant/frontend
|
||||
homeassistant/components/luci/* @fbradyirl @mzdrale
|
||||
homeassistant/components/luci/* @mzdrale
|
||||
homeassistant/components/luftdaten/* @fabaff
|
||||
homeassistant/components/lupusec/* @majuss
|
||||
homeassistant/components/lutron/* @JonGilmore
|
||||
@ -261,7 +264,7 @@ homeassistant/components/min_max/* @fabaff
|
||||
homeassistant/components/minecraft_server/* @elmurato
|
||||
homeassistant/components/minio/* @tkislan
|
||||
homeassistant/components/mobile_app/* @robbiet480
|
||||
homeassistant/components/modbus/* @adamchengtkc @janiversen
|
||||
homeassistant/components/modbus/* @adamchengtkc @janiversen @vzahradnik
|
||||
homeassistant/components/monoprice/* @etsinko @OnFreund
|
||||
homeassistant/components/moon/* @fabaff
|
||||
homeassistant/components/mpd/* @fabaff
|
||||
@ -300,6 +303,7 @@ homeassistant/components/nzbget/* @chriscla
|
||||
homeassistant/components/obihai/* @dshokouhi
|
||||
homeassistant/components/ohmconnect/* @robbiet480
|
||||
homeassistant/components/ombi/* @larssont
|
||||
homeassistant/components/omnilogic/* @oliver84 @djtimca @gentoosu
|
||||
homeassistant/components/onboarding/* @home-assistant/core
|
||||
homeassistant/components/onewire/* @garbled1
|
||||
homeassistant/components/onvif/* @hunterjm
|
||||
@ -349,6 +353,7 @@ homeassistant/components/raincloud/* @vanstinator
|
||||
homeassistant/components/rainforest_eagle/* @gtdiehl @jcalbert
|
||||
homeassistant/components/rainmachine/* @bachya
|
||||
homeassistant/components/random/* @fabaff
|
||||
homeassistant/components/rejseplanen/* @DarkFox
|
||||
homeassistant/components/repetier/* @MTrab
|
||||
homeassistant/components/rfxtrx/* @danielhiversen @elupus
|
||||
homeassistant/components/ring/* @balloob
|
||||
@ -357,6 +362,7 @@ homeassistant/components/rmvtransport/* @cgtobi
|
||||
homeassistant/components/roku/* @ctalkington
|
||||
homeassistant/components/roomba/* @pschmitt @cyr-ius @shenxn
|
||||
homeassistant/components/roon/* @pavoni
|
||||
homeassistant/components/rpi_power/* @shenxn @swetoast
|
||||
homeassistant/components/safe_mode/* @home-assistant/core
|
||||
homeassistant/components/saj/* @fredericvl
|
||||
homeassistant/components/salt/* @bjornorri
|
||||
@ -399,9 +405,11 @@ homeassistant/components/soma/* @ratsept
|
||||
homeassistant/components/somfy/* @tetienne
|
||||
homeassistant/components/sonarr/* @ctalkington
|
||||
homeassistant/components/songpal/* @rytilahti @shenxn
|
||||
homeassistant/components/sonos/* @cgtobi
|
||||
homeassistant/components/spaceapi/* @fabaff
|
||||
homeassistant/components/speedtestdotnet/* @rohankapoorcom @engrbm87
|
||||
homeassistant/components/spider/* @peternijssen
|
||||
homeassistant/components/splunk/* @Bre77
|
||||
homeassistant/components/spotify/* @frenck
|
||||
homeassistant/components/sql/* @dgomes
|
||||
homeassistant/components/squeezebox/* @rajlaud
|
||||
@ -421,7 +429,7 @@ homeassistant/components/switchbot/* @danielhiversen
|
||||
homeassistant/components/switcher_kis/* @tomerfi
|
||||
homeassistant/components/switchmate/* @danielhiversen
|
||||
homeassistant/components/syncthru/* @nielstron
|
||||
homeassistant/components/synology_dsm/* @ProtoThis @Quentame
|
||||
homeassistant/components/synology_dsm/* @hacf-fr @Quentame
|
||||
homeassistant/components/synology_srm/* @aerialls
|
||||
homeassistant/components/syslog/* @fabaff
|
||||
homeassistant/components/tado/* @michaelarnauts @bdraco
|
||||
@ -502,6 +510,7 @@ homeassistant/components/yi/* @bachya
|
||||
homeassistant/components/zeroconf/* @Kane610
|
||||
homeassistant/components/zerproc/* @emlove
|
||||
homeassistant/components/zha/* @dmulcahey @adminiuga
|
||||
homeassistant/components/zodiac/* @JulienTant
|
||||
homeassistant/components/zone/* @home-assistant/core
|
||||
homeassistant/components/zoneminder/* @rohankapoorcom
|
||||
homeassistant/components/zwave/* @home-assistant/z-wave
|
||||
|
@ -5,6 +5,7 @@ from homeassistant.const import (
|
||||
LENGTH_FEET,
|
||||
LENGTH_INCHES,
|
||||
LENGTH_METERS,
|
||||
LENGTH_MILLIMETERS,
|
||||
PERCENTAGE,
|
||||
SPEED_KILOMETERS_PER_HOUR,
|
||||
SPEED_MILES_PER_HOUR,
|
||||
@ -24,7 +25,6 @@ ATTR_UNIT_METRIC = "Metric"
|
||||
CONCENTRATION_PARTS_PER_CUBIC_METER = f"p/{VOLUME_CUBIC_METERS}"
|
||||
COORDINATOR = "coordinator"
|
||||
DOMAIN = "accuweather"
|
||||
LENGTH_MILIMETERS = "mm"
|
||||
MANUFACTURER = "AccuWeather, Inc."
|
||||
NAME = "AccuWeather"
|
||||
UNDO_UPDATE_LISTENER = "undo_update_listener"
|
||||
@ -238,7 +238,7 @@ SENSOR_TYPES = {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:weather-rainy",
|
||||
ATTR_LABEL: "Precipitation",
|
||||
ATTR_UNIT_METRIC: LENGTH_MILIMETERS,
|
||||
ATTR_UNIT_METRIC: LENGTH_MILLIMETERS,
|
||||
ATTR_UNIT_IMPERIAL: LENGTH_INCHES,
|
||||
},
|
||||
"PressureTendency": {
|
||||
|
13
homeassistant/components/accuweather/translations/de.json
Normal file
13
homeassistant/components/accuweather/translations/de.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"latitude": "Breitengrad",
|
||||
"longitude": "L\u00e4ngengrad"
|
||||
},
|
||||
"title": "AccuWeather"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
homeassistant/components/accuweather/translations/et.json
Normal file
35
homeassistant/components/accuweather/translations/et.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"single_instance_allowed": "Sidumine juba tehtud. V\u00f5imalik on ainult 1 sidumine,"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00dchendus eba\u00f5nnestus",
|
||||
"invalid_api_key": "API v\u00f5ti on vale",
|
||||
"requests_exceeded": "Accuweatheri API-le esitatud p\u00e4ringute piirarv on \u00fcletatud. Peate ootama (v\u00f5i muutma API v\u00f5tit)."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API v\u00f5ti",
|
||||
"latitude": "Laiuskraad",
|
||||
"longitude": "Pikkuskraad",
|
||||
"name": "Sidumise nimi"
|
||||
},
|
||||
"description": "Kui vajate seadistamisel abi vaadake siit: https://www.home-assistant.io/integrations/accuweather/ \n\n M\u00f5ni andur pole vaikimisi lubatud. P\u00e4rast sidumise seadistamist saate need \u00fcksused lubada. \n Ilmapennustus pole vaikimisi lubatud. Saate selle lubada sidumise s\u00e4tetes.",
|
||||
"title": "AccuWeather"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"forecast": "Ilmateade"
|
||||
},
|
||||
"description": "AccuWeather API tasuta versioonis toimub ilmaennustuse lubamisel andmete v\u00e4rskendamine iga 32 minuti asemel iga 64 minuti j\u00e4rel.",
|
||||
"title": "AccuWeatheri valikud"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,9 +3,32 @@
|
||||
"abort": {
|
||||
"single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_api_key": "Cl\u00e9 API invalide",
|
||||
"requests_exceeded": "Le nombre autoris\u00e9 de requ\u00eates adress\u00e9es \u00e0 l'API AccuWeather a \u00e9t\u00e9 d\u00e9pass\u00e9. Vous devez attendre ou modifier la cl\u00e9 API."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "Si vous avez besoin d'aide pour la configuration, consultez le site suivant : https://www.home-assistant.io/integrations/accuweather/\n\nCertains capteurs ne sont pas activ\u00e9s par d\u00e9faut. Vous pouvez les activer dans le registre des entit\u00e9s apr\u00e8s la configuration de l'int\u00e9gration.\nLes pr\u00e9visions m\u00e9t\u00e9orologiques ne sont pas activ\u00e9es par d\u00e9faut. Vous pouvez l'activer dans les options d'int\u00e9gration."
|
||||
"data": {
|
||||
"api_key": "Cl\u00e9 d'API",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"name": "Nom de l'int\u00e9gration"
|
||||
},
|
||||
"description": "Si vous avez besoin d'aide pour la configuration, consultez le site suivant : https://www.home-assistant.io/integrations/accuweather/\n\nCertains capteurs ne sont pas activ\u00e9s par d\u00e9faut. Vous pouvez les activer dans le registre des entit\u00e9s apr\u00e8s la configuration de l'int\u00e9gration.\nLes pr\u00e9visions m\u00e9t\u00e9orologiques ne sont pas activ\u00e9es par d\u00e9faut. Vous pouvez l'activer dans les options d'int\u00e9gration.",
|
||||
"title": "AccuWeather"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"forecast": "Pr\u00e9visions m\u00e9t\u00e9orologiques"
|
||||
},
|
||||
"description": "En raison des limitations de la version gratuite de la cl\u00e9 API AccuWeather, lorsque vous activez les pr\u00e9visions m\u00e9t\u00e9orologiques, les mises \u00e0 jour des donn\u00e9es seront effectu\u00e9es toutes les 64 minutes au lieu de toutes les 32 minutes.",
|
||||
"title": "Options AccuWeather"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,8 @@
|
||||
"longitude": "Lengdegrad",
|
||||
"name": "Navn p\u00e5 integrasjon"
|
||||
},
|
||||
"description": "Hvis du trenger hjelp med konfigurasjonen, kan du se her: https://www.home-assistant.io/integrations/accuweather/ \n\n Noen sensorer er ikke aktivert som standard. Du kan aktivere dem i enhetsregisteret etter integrasjonskonfigurasjonen. \n V\u00e6rmelding er ikke aktivert som standard. Du kan aktivere det i integrasjonsalternativene."
|
||||
"description": "Hvis du trenger hjelp med konfigurasjonen, kan du se her: https://www.home-assistant.io/integrations/accuweather/ \n\n Noen sensorer er ikke aktivert som standard. Du kan aktivere dem i enhetsregisteret etter integrasjonskonfigurasjonen. \n V\u00e6rmelding er ikke aktivert som standard. Du kan aktivere det i integrasjonsalternativene.",
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -4,7 +4,7 @@
|
||||
"single_instance_allowed": "Ju\u017c skonfigurowano. Mo\u017cliwa jest tylko jedna konfiguracja."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia.",
|
||||
"cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia",
|
||||
"invalid_api_key": "Nieprawid\u0142owy klucz API.",
|
||||
"requests_exceeded": "Dozwolona liczba zapyta\u0144 do interfejsu API AccuWeather zosta\u0142a przekroczona. Musisz poczeka\u0107 lub zmieni\u0107 klucz API."
|
||||
},
|
||||
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"state": {
|
||||
"accuweather__pressure_tendency": {
|
||||
"falling": "Langev",
|
||||
"rising": "T\u00f5usev",
|
||||
"steady": "\u00dchtlane"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"state": {
|
||||
"accuweather__pressure_tendency": {
|
||||
"falling": "En baisse",
|
||||
"rising": "En hausse",
|
||||
"steady": "Stable"
|
||||
}
|
||||
}
|
||||
}
|
@ -11,5 +11,6 @@
|
||||
"title": "Velg en hub du vil legge til"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": ""
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
"data": {
|
||||
"host": "Vert",
|
||||
"password": "Passord",
|
||||
"port": "",
|
||||
"ssl": "AdGuard Hjem bruker et SSL-sertifikat",
|
||||
"username": "Brukernavn",
|
||||
"verify_ssl": "AdGuard Home bruker et riktig sertifikat"
|
||||
|
@ -5,7 +5,7 @@
|
||||
"single_instance_allowed": "Dozwolona jest tylko jedna konfiguracja AdGuard Home."
|
||||
},
|
||||
"error": {
|
||||
"connection_error": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia."
|
||||
"connection_error": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia"
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
|
@ -4,6 +4,7 @@ import logging
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
DEVICE_CLASS_MOVING,
|
||||
DEVICE_CLASSES_SCHEMA,
|
||||
PLATFORM_SCHEMA,
|
||||
BinarySensorEntity,
|
||||
@ -43,7 +44,7 @@ class AdsBinarySensor(AdsEntity, BinarySensorEntity):
|
||||
def __init__(self, ads_hub, name, ads_var, device_class):
|
||||
"""Initialize ADS binary sensor."""
|
||||
super().__init__(ads_hub, name, ads_var)
|
||||
self._device_class = device_class or "moving"
|
||||
self._device_class = device_class or DEVICE_CLASS_MOVING
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register device notification."""
|
||||
|
@ -10,7 +10,8 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Vert"
|
||||
"host": "Vert",
|
||||
"port": ""
|
||||
},
|
||||
"title": "Konfigurere Agent DVR"
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane."
|
||||
"already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane"
|
||||
},
|
||||
"error": {
|
||||
"already_in_progress": "Konfiguracja urz\u0105dzenia jest ju\u017c w toku.",
|
||||
|
14
homeassistant/components/air_quality/group.py
Normal file
14
homeassistant/components/air_quality/group.py
Normal file
@ -0,0 +1,14 @@
|
||||
"""Describe group states."""
|
||||
|
||||
|
||||
from homeassistant.components.group import GroupIntegrationRegistry
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
|
||||
@callback
|
||||
def async_describe_on_off_states(
|
||||
hass: HomeAssistantType, registry: GroupIntegrationRegistry
|
||||
) -> None:
|
||||
"""Describe group on off states."""
|
||||
registry.exclude_domain()
|
12
homeassistant/components/airly/translations/et.json
Normal file
12
homeassistant/components/airly/translations/et.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"latitude": "Laiuskraad",
|
||||
"longitude": "Pikkuskraad"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,8 @@
|
||||
"longitude": "Lengdegrad",
|
||||
"name": "Navn p\u00e5 integrasjonen"
|
||||
},
|
||||
"description": "Sett opp Airly luftkvalitet integrasjon. For \u00e5 opprette API-n\u00f8kkel, g\u00e5 til [https://developer.airly.eu/register](https://developer.airly.eu/register)"
|
||||
"description": "Sett opp Airly luftkvalitet integrasjon. For \u00e5 opprette API-n\u00f8kkel, g\u00e5 til [https://developer.airly.eu/register](https://developer.airly.eu/register)",
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,11 @@
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude"
|
||||
}
|
||||
},
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,13 @@ import asyncio
|
||||
from datetime import timedelta
|
||||
from math import ceil
|
||||
|
||||
from pyairvisual import Client
|
||||
from pyairvisual.errors import AirVisualError, NodeProError
|
||||
from pyairvisual import CloudAPI, NodeSamba
|
||||
from pyairvisual.errors import (
|
||||
AirVisualError,
|
||||
InvalidKeyError,
|
||||
KeyExpiredError,
|
||||
NodeProError,
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import SOURCE_IMPORT
|
||||
@ -206,29 +211,36 @@ def _standardize_node_pro_config_entry(hass, config_entry):
|
||||
|
||||
async def async_setup_entry(hass, config_entry):
|
||||
"""Set up AirVisual as config entry."""
|
||||
websession = aiohttp_client.async_get_clientsession(hass)
|
||||
|
||||
if CONF_API_KEY in config_entry.data:
|
||||
_standardize_geography_config_entry(hass, config_entry)
|
||||
|
||||
client = Client(api_key=config_entry.data[CONF_API_KEY], session=websession)
|
||||
websession = aiohttp_client.async_get_clientsession(hass)
|
||||
cloud_api = CloudAPI(config_entry.data[CONF_API_KEY], session=websession)
|
||||
|
||||
async def async_update_data():
|
||||
"""Get new data from the API."""
|
||||
if CONF_CITY in config_entry.data:
|
||||
api_coro = client.api.city(
|
||||
api_coro = cloud_api.air_quality.city(
|
||||
config_entry.data[CONF_CITY],
|
||||
config_entry.data[CONF_STATE],
|
||||
config_entry.data[CONF_COUNTRY],
|
||||
)
|
||||
else:
|
||||
api_coro = client.api.nearest_city(
|
||||
api_coro = cloud_api.air_quality.nearest_city(
|
||||
config_entry.data[CONF_LATITUDE],
|
||||
config_entry.data[CONF_LONGITUDE],
|
||||
)
|
||||
|
||||
try:
|
||||
return await api_coro
|
||||
except (InvalidKeyError, KeyExpiredError):
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": "reauth"},
|
||||
data=config_entry.data,
|
||||
)
|
||||
)
|
||||
except AirVisualError as err:
|
||||
raise UpdateFailed(f"Error while retrieving data: {err}") from err
|
||||
|
||||
@ -254,17 +266,13 @@ async def async_setup_entry(hass, config_entry):
|
||||
else:
|
||||
_standardize_node_pro_config_entry(hass, config_entry)
|
||||
|
||||
client = Client(session=websession)
|
||||
|
||||
async def async_update_data():
|
||||
"""Get new data from the API."""
|
||||
try:
|
||||
return await client.node.from_samba(
|
||||
config_entry.data[CONF_IP_ADDRESS],
|
||||
config_entry.data[CONF_PASSWORD],
|
||||
include_history=False,
|
||||
include_trends=False,
|
||||
)
|
||||
async with NodeSamba(
|
||||
config_entry.data[CONF_IP_ADDRESS], config_entry.data[CONF_PASSWORD]
|
||||
) as node:
|
||||
return await node.async_get_latest_measurements()
|
||||
except NodeProError as err:
|
||||
raise UpdateFailed(f"Error while retrieving data: {err}") from err
|
||||
|
||||
|
@ -40,9 +40,9 @@ class AirVisualNodeProSensor(AirVisualEntity, AirQualityEntity):
|
||||
@property
|
||||
def air_quality_index(self):
|
||||
"""Return the Air Quality Index (AQI)."""
|
||||
if self.coordinator.data["current"]["settings"]["is_aqi_usa"]:
|
||||
return self.coordinator.data["current"]["measurements"]["aqi_us"]
|
||||
return self.coordinator.data["current"]["measurements"]["aqi_cn"]
|
||||
if self.coordinator.data["settings"]["is_aqi_usa"]:
|
||||
return self.coordinator.data["measurements"]["aqi_us"]
|
||||
return self.coordinator.data["measurements"]["aqi_cn"]
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
@ -52,61 +52,59 @@ class AirVisualNodeProSensor(AirVisualEntity, AirQualityEntity):
|
||||
@property
|
||||
def carbon_dioxide(self):
|
||||
"""Return the CO2 (carbon dioxide) level."""
|
||||
return self.coordinator.data["current"]["measurements"].get("co2")
|
||||
return self.coordinator.data["measurements"].get("co2")
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return device registry information for this entity."""
|
||||
return {
|
||||
"identifiers": {
|
||||
(DOMAIN, self.coordinator.data["current"]["serial_number"])
|
||||
},
|
||||
"name": self.coordinator.data["current"]["settings"]["node_name"],
|
||||
"identifiers": {(DOMAIN, self.coordinator.data["serial_number"])},
|
||||
"name": self.coordinator.data["settings"]["node_name"],
|
||||
"manufacturer": "AirVisual",
|
||||
"model": f'{self.coordinator.data["current"]["status"]["model"]}',
|
||||
"model": f'{self.coordinator.data["status"]["model"]}',
|
||||
"sw_version": (
|
||||
f'Version {self.coordinator.data["current"]["status"]["system_version"]}'
|
||||
f'{self.coordinator.data["current"]["status"]["app_version"]}'
|
||||
f'Version {self.coordinator.data["status"]["system_version"]}'
|
||||
f'{self.coordinator.data["status"]["app_version"]}'
|
||||
),
|
||||
}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name."""
|
||||
node_name = self.coordinator.data["current"]["settings"]["node_name"]
|
||||
node_name = self.coordinator.data["settings"]["node_name"]
|
||||
return f"{node_name} Node/Pro: Air Quality"
|
||||
|
||||
@property
|
||||
def particulate_matter_2_5(self):
|
||||
"""Return the particulate matter 2.5 level."""
|
||||
return self.coordinator.data["current"]["measurements"].get("pm2_5")
|
||||
return self.coordinator.data["measurements"].get("pm2_5")
|
||||
|
||||
@property
|
||||
def particulate_matter_10(self):
|
||||
"""Return the particulate matter 10 level."""
|
||||
return self.coordinator.data["current"]["measurements"].get("pm1_0")
|
||||
return self.coordinator.data["measurements"].get("pm1_0")
|
||||
|
||||
@property
|
||||
def particulate_matter_0_1(self):
|
||||
"""Return the particulate matter 0.1 level."""
|
||||
return self.coordinator.data["current"]["measurements"].get("pm0_1")
|
||||
return self.coordinator.data["measurements"].get("pm0_1")
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return a unique, Home Assistant friendly identifier for this entity."""
|
||||
return self.coordinator.data["current"]["serial_number"]
|
||||
return self.coordinator.data["serial_number"]
|
||||
|
||||
@callback
|
||||
def update_from_latest_data(self):
|
||||
"""Update the entity from the latest data."""
|
||||
self._attrs.update(
|
||||
{
|
||||
ATTR_VOC: self.coordinator.data["current"]["measurements"].get("voc"),
|
||||
ATTR_VOC: self.coordinator.data["measurements"].get("voc"),
|
||||
**{
|
||||
ATTR_SENSOR_LIFE.format(pollutant): lifespan
|
||||
for pollutant, lifespan in self.coordinator.data["current"][
|
||||
"status"
|
||||
]["sensor_life"].items()
|
||||
for pollutant, lifespan in self.coordinator.data["status"][
|
||||
"sensor_life"
|
||||
].items()
|
||||
},
|
||||
}
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""Define a config flow manager for AirVisual."""
|
||||
import asyncio
|
||||
|
||||
from pyairvisual import Client
|
||||
from pyairvisual import CloudAPI, NodeSamba
|
||||
from pyairvisual.errors import InvalidKeyError, NodeProError
|
||||
import voluptuous as vol
|
||||
|
||||
@ -34,12 +34,19 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
VERSION = 2
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the config flow."""
|
||||
self._geo_id = None
|
||||
self._latitude = None
|
||||
self._longitude = None
|
||||
|
||||
self.api_key_data_schema = vol.Schema({vol.Required(CONF_API_KEY): str})
|
||||
|
||||
@property
|
||||
def geography_schema(self):
|
||||
"""Return the data schema for the cloud API."""
|
||||
return vol.Schema(
|
||||
return self.api_key_data_schema.extend(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): str,
|
||||
vol.Required(
|
||||
CONF_LATITUDE, default=self.hass.config.latitude
|
||||
): cv.latitude,
|
||||
@ -85,8 +92,8 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
step_id="geography", data_schema=self.geography_schema
|
||||
)
|
||||
|
||||
geo_id = async_get_geography_id(user_input)
|
||||
await self._async_set_unique_id(geo_id)
|
||||
self._geo_id = async_get_geography_id(user_input)
|
||||
await self._async_set_unique_id(self._geo_id)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
# Find older config entries without unique ID:
|
||||
@ -95,13 +102,13 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
continue
|
||||
|
||||
if any(
|
||||
geo_id == async_get_geography_id(geography)
|
||||
self._geo_id == async_get_geography_id(geography)
|
||||
for geography in entry.data[CONF_GEOGRAPHIES]
|
||||
):
|
||||
return self.async_abort(reason="already_configured")
|
||||
|
||||
websession = aiohttp_client.async_get_clientsession(self.hass)
|
||||
client = Client(session=websession, api_key=user_input[CONF_API_KEY])
|
||||
cloud_api = CloudAPI(user_input[CONF_API_KEY], session=websession)
|
||||
|
||||
# If this is the first (and only the first) time we've seen this API key, check
|
||||
# that it's valid:
|
||||
@ -113,7 +120,7 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
async with check_keys_lock:
|
||||
if user_input[CONF_API_KEY] not in checked_keys:
|
||||
try:
|
||||
await client.api.nearest_city()
|
||||
await cloud_api.air_quality.nearest_city()
|
||||
except InvalidKeyError:
|
||||
return self.async_show_form(
|
||||
step_id="geography",
|
||||
@ -123,10 +130,19 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
checked_keys.add(user_input[CONF_API_KEY])
|
||||
|
||||
return self.async_create_entry(
|
||||
title=f"Cloud API ({geo_id})",
|
||||
data={**user_input, CONF_INTEGRATION_TYPE: INTEGRATION_TYPE_GEOGRAPHY},
|
||||
)
|
||||
return await self.async_step_geography_finish(user_input)
|
||||
|
||||
async def async_step_geography_finish(self, user_input=None):
|
||||
"""Handle the finalization of a Cloud API config entry."""
|
||||
existing_entry = await self.async_set_unique_id(self._geo_id)
|
||||
if existing_entry:
|
||||
self.hass.config_entries.async_update_entry(existing_entry, data=user_input)
|
||||
return self.async_abort(reason="reauth_successful")
|
||||
|
||||
return self.async_create_entry(
|
||||
title=f"Cloud API ({self._geo_id})",
|
||||
data={**user_input, CONF_INTEGRATION_TYPE: INTEGRATION_TYPE_GEOGRAPHY},
|
||||
)
|
||||
|
||||
async def async_step_import(self, import_config):
|
||||
"""Import a config entry from configuration.yaml."""
|
||||
@ -141,16 +157,10 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
await self._async_set_unique_id(user_input[CONF_IP_ADDRESS])
|
||||
|
||||
websession = aiohttp_client.async_get_clientsession(self.hass)
|
||||
client = Client(session=websession)
|
||||
node = NodeSamba(user_input[CONF_IP_ADDRESS], user_input[CONF_PASSWORD])
|
||||
|
||||
try:
|
||||
await client.node.from_samba(
|
||||
user_input[CONF_IP_ADDRESS],
|
||||
user_input[CONF_PASSWORD],
|
||||
include_history=False,
|
||||
include_trends=False,
|
||||
)
|
||||
await node.async_connect()
|
||||
except NodeProError as err:
|
||||
LOGGER.error("Error connecting to Node/Pro unit: %s", err)
|
||||
return self.async_show_form(
|
||||
@ -159,11 +169,37 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
errors={CONF_IP_ADDRESS: "unable_to_connect"},
|
||||
)
|
||||
|
||||
await node.async_disconnect()
|
||||
|
||||
return self.async_create_entry(
|
||||
title=f"Node/Pro ({user_input[CONF_IP_ADDRESS]})",
|
||||
data={**user_input, CONF_INTEGRATION_TYPE: INTEGRATION_TYPE_NODE_PRO},
|
||||
)
|
||||
|
||||
async def async_step_reauth(self, data):
|
||||
"""Handle configuration by re-auth."""
|
||||
self._latitude = data[CONF_LATITUDE]
|
||||
self._longitude = data[CONF_LONGITUDE]
|
||||
|
||||
return await self.async_step_reauth_confirm()
|
||||
|
||||
async def async_step_reauth_confirm(self, user_input=None):
|
||||
"""Handle re-auth completion."""
|
||||
if not user_input:
|
||||
return self.async_show_form(
|
||||
step_id="reauth_confirm", data_schema=self.api_key_data_schema
|
||||
)
|
||||
|
||||
conf = {
|
||||
CONF_API_KEY: user_input[CONF_API_KEY],
|
||||
CONF_LATITUDE: self._latitude,
|
||||
CONF_LONGITUDE: self._longitude,
|
||||
}
|
||||
|
||||
self._geo_id = async_get_geography_id(conf)
|
||||
|
||||
return await self.async_step_geography_finish(conf)
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle the start of the config flow."""
|
||||
if not user_input:
|
||||
|
@ -3,6 +3,6 @@
|
||||
"name": "AirVisual",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/airvisual",
|
||||
"requirements": ["pyairvisual==4.4.0"],
|
||||
"requirements": ["pyairvisual==5.0.2"],
|
||||
"codeowners": ["@bachya"]
|
||||
}
|
||||
|
@ -38,10 +38,6 @@ ATTR_POLLUTANT_SYMBOL = "pollutant_symbol"
|
||||
ATTR_POLLUTANT_UNIT = "pollutant_unit"
|
||||
ATTR_REGION = "region"
|
||||
|
||||
MASS_PARTS_PER_MILLION = "ppm"
|
||||
MASS_PARTS_PER_BILLION = "ppb"
|
||||
VOLUME_MICROGRAMS_PER_CUBIC_METER = "µg/m3"
|
||||
|
||||
SENSOR_KIND_LEVEL = "air_pollution_level"
|
||||
SENSOR_KIND_AQI = "air_quality_index"
|
||||
SENSOR_KIND_POLLUTANT = "main_pollutant"
|
||||
@ -229,22 +225,20 @@ class AirVisualNodeProSensor(AirVisualEntity):
|
||||
def device_info(self):
|
||||
"""Return device registry information for this entity."""
|
||||
return {
|
||||
"identifiers": {
|
||||
(DOMAIN, self.coordinator.data["current"]["serial_number"])
|
||||
},
|
||||
"name": self.coordinator.data["current"]["settings"]["node_name"],
|
||||
"identifiers": {(DOMAIN, self.coordinator.data["serial_number"])},
|
||||
"name": self.coordinator.data["settings"]["node_name"],
|
||||
"manufacturer": "AirVisual",
|
||||
"model": f'{self.coordinator.data["current"]["status"]["model"]}',
|
||||
"model": f'{self.coordinator.data["status"]["model"]}',
|
||||
"sw_version": (
|
||||
f'Version {self.coordinator.data["current"]["status"]["system_version"]}'
|
||||
f'{self.coordinator.data["current"]["status"]["app_version"]}'
|
||||
f'Version {self.coordinator.data["status"]["system_version"]}'
|
||||
f'{self.coordinator.data["status"]["app_version"]}'
|
||||
),
|
||||
}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name."""
|
||||
node_name = self.coordinator.data["current"]["settings"]["node_name"]
|
||||
node_name = self.coordinator.data["settings"]["node_name"]
|
||||
return f"{node_name} Node/Pro: {self._name}"
|
||||
|
||||
@property
|
||||
@ -255,18 +249,14 @@ class AirVisualNodeProSensor(AirVisualEntity):
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return a unique, Home Assistant friendly identifier for this entity."""
|
||||
return f"{self.coordinator.data['current']['serial_number']}_{self._kind}"
|
||||
return f"{self.coordinator.data['serial_number']}_{self._kind}"
|
||||
|
||||
@callback
|
||||
def update_from_latest_data(self):
|
||||
"""Update the entity from the latest data."""
|
||||
if self._kind == SENSOR_KIND_BATTERY_LEVEL:
|
||||
self._state = self.coordinator.data["current"]["status"]["battery"]
|
||||
self._state = self.coordinator.data["status"]["battery"]
|
||||
elif self._kind == SENSOR_KIND_HUMIDITY:
|
||||
self._state = self.coordinator.data["current"]["measurements"].get(
|
||||
"humidity"
|
||||
)
|
||||
self._state = self.coordinator.data["measurements"].get("humidity")
|
||||
elif self._kind == SENSOR_KIND_TEMPERATURE:
|
||||
self._state = self.coordinator.data["current"]["measurements"].get(
|
||||
"temperature_C"
|
||||
)
|
||||
self._state = self.coordinator.data["measurements"].get("temperature_C")
|
||||
|
17
homeassistant/components/airvisual/translations/et.json
Normal file
17
homeassistant/components/airvisual/translations/et.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"geography": {
|
||||
"data": {
|
||||
"latitude": "Laiuskraad",
|
||||
"longitude": "Pikkuskraad"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"cloud_api": "Geograafiline asukoht"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@
|
||||
"user": {
|
||||
"data": {
|
||||
"cloud_api": "Geografisk plassering",
|
||||
"node_pro": "",
|
||||
"type": "Integrasjonstype"
|
||||
},
|
||||
"description": "Velg hvilken type AirVisual-data du vil overv\u00e5ke.",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"already_configured": "Ten klucz API jest ju\u017c w u\u017cyciu."
|
||||
},
|
||||
"error": {
|
||||
"general_error": "Nieoczekiwany b\u0142\u0105d.",
|
||||
"general_error": "Nieoczekiwany b\u0142\u0105d",
|
||||
"invalid_api_key": "Nieprawid\u0142owy klucz API.",
|
||||
"unable_to_connect": "Nie mo\u017cna po\u0142\u0105czy\u0107 si\u0119 z jednostk\u0105 Node/Pro."
|
||||
},
|
||||
|
@ -6,6 +6,7 @@ import voluptuous as vol
|
||||
from homeassistant.const import (
|
||||
ATTR_CODE,
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
CONF_CODE,
|
||||
CONF_DEVICE_ID,
|
||||
CONF_DOMAIN,
|
||||
@ -56,7 +57,7 @@ async def async_get_actions(hass: HomeAssistant, device_id: str) -> List[dict]:
|
||||
if state is None:
|
||||
continue
|
||||
|
||||
supported_features = state.attributes["supported_features"]
|
||||
supported_features = state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||
|
||||
# Add actions for each entity that belongs to this integration
|
||||
if supported_features & SUPPORT_ALARM_ARM_AWAY:
|
||||
|
@ -11,6 +11,7 @@ from homeassistant.components.alarm_control_panel.const import (
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
CONF_CONDITION,
|
||||
CONF_DEVICE_ID,
|
||||
CONF_DOMAIN,
|
||||
@ -73,7 +74,7 @@ async def async_get_conditions(
|
||||
if state is None:
|
||||
continue
|
||||
|
||||
supported_features = state.attributes["supported_features"]
|
||||
supported_features = state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||
|
||||
# Add conditions for each entity that belongs to this integration
|
||||
conditions += [
|
||||
|
@ -12,6 +12,7 @@ from homeassistant.components.automation import AutomationActionType
|
||||
from homeassistant.components.device_automation import TRIGGER_BASE_SCHEMA
|
||||
from homeassistant.components.homeassistant.triggers import state as state_trigger
|
||||
from homeassistant.const import (
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
CONF_DEVICE_ID,
|
||||
CONF_DOMAIN,
|
||||
CONF_ENTITY_ID,
|
||||
@ -64,7 +65,7 @@ async def async_get_triggers(hass: HomeAssistant, device_id: str) -> List[dict]:
|
||||
if entity_state is None:
|
||||
continue
|
||||
|
||||
supported_features = entity_state.attributes["supported_features"]
|
||||
supported_features = entity_state.attributes[ATTR_SUPPORTED_FEATURES]
|
||||
|
||||
# Add triggers for each entity that belongs to this integration
|
||||
triggers += [
|
||||
|
31
homeassistant/components/alarm_control_panel/group.py
Normal file
31
homeassistant/components/alarm_control_panel/group.py
Normal file
@ -0,0 +1,31 @@
|
||||
"""Describe group states."""
|
||||
|
||||
|
||||
from homeassistant.components.group import GroupIntegrationRegistry
|
||||
from homeassistant.const import (
|
||||
STATE_ALARM_ARMED_AWAY,
|
||||
STATE_ALARM_ARMED_CUSTOM_BYPASS,
|
||||
STATE_ALARM_ARMED_HOME,
|
||||
STATE_ALARM_ARMED_NIGHT,
|
||||
STATE_ALARM_TRIGGERED,
|
||||
STATE_OFF,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
|
||||
@callback
|
||||
def async_describe_on_off_states(
|
||||
hass: HomeAssistantType, registry: GroupIntegrationRegistry
|
||||
) -> None:
|
||||
"""Describe group on off states."""
|
||||
registry.on_off_states(
|
||||
{
|
||||
STATE_ALARM_ARMED_AWAY,
|
||||
STATE_ALARM_ARMED_CUSTOM_BYPASS,
|
||||
STATE_ALARM_ARMED_HOME,
|
||||
STATE_ALARM_ARMED_NIGHT,
|
||||
STATE_ALARM_TRIGGERED,
|
||||
},
|
||||
STATE_OFF,
|
||||
)
|
@ -1,4 +1,27 @@
|
||||
{
|
||||
"device_automation": {
|
||||
"action_type": {
|
||||
"arm_away": "Valvesta {entity_name}",
|
||||
"arm_home": "Valvesta {entity_name} kodus re\u017eiimis",
|
||||
"arm_night": "Valvesta {entity_name} \u00f6\u00f6re\u017eiimis",
|
||||
"disarm": "V\u00f5ta {entity_name} valvest maha",
|
||||
"trigger": "K\u00e4ivita {entity_name}"
|
||||
},
|
||||
"condition_type": {
|
||||
"is_armed_away": "{entity_name} on valvestatud",
|
||||
"is_armed_home": "{entity_name} on valvestatud kodure\u017eiimis",
|
||||
"is_armed_night": "{entity_name} on valvestatud \u00f6\u00f6re\u017eiimis",
|
||||
"is_disarmed": "{entity_name} on valve alt maas",
|
||||
"is_triggered": "{entity_name} on h\u00e4iret andnud"
|
||||
},
|
||||
"trigger_type": {
|
||||
"armed_away": "{entity_name} valvestatus",
|
||||
"armed_home": "{entity_name} valvestatus kodure\u017eiimis",
|
||||
"armed_night": "{entity_name} valvestatus \u00f6\u00f6re\u017eiimis",
|
||||
"disarmed": "{entity_name} v\u00f5eti valvest maha",
|
||||
"triggered": "{entity_name} andis h\u00e4iret"
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
"_": {
|
||||
"armed": "Valves",
|
||||
|
@ -25,7 +25,7 @@
|
||||
"state": {
|
||||
"_": {
|
||||
"armed": "Ingeschakeld",
|
||||
"armed_away": "Afwezig Ingeschakeld",
|
||||
"armed_away": "Ingeschakeld afwezig",
|
||||
"armed_custom_bypass": "Ingeschakeld met overbrugging(en)",
|
||||
"armed_home": "Ingeschakeld thuis",
|
||||
"armed_night": "Ingeschakeld nacht",
|
||||
|
@ -1,167 +1,82 @@
|
||||
"""Support for AlarmDecoder devices."""
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from adext import AdExt
|
||||
from alarmdecoder.devices import SerialDevice, SocketDevice, USBDevice
|
||||
from alarmdecoder.devices import SerialDevice, SocketDevice
|
||||
from alarmdecoder.util import NoDeviceError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.binary_sensor import DEVICE_CLASSES_SCHEMA
|
||||
from homeassistant.const import CONF_HOST, EVENT_HOMEASSISTANT_STOP
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.discovery import load_platform
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_PORT,
|
||||
CONF_PROTOCOL,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
)
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import (
|
||||
CONF_DEVICE_BAUD,
|
||||
CONF_DEVICE_PATH,
|
||||
DATA_AD,
|
||||
DATA_REMOVE_STOP_LISTENER,
|
||||
DATA_REMOVE_UPDATE_LISTENER,
|
||||
DATA_RESTART,
|
||||
DOMAIN,
|
||||
PROTOCOL_SERIAL,
|
||||
PROTOCOL_SOCKET,
|
||||
SIGNAL_PANEL_MESSAGE,
|
||||
SIGNAL_REL_MESSAGE,
|
||||
SIGNAL_RFX_MESSAGE,
|
||||
SIGNAL_ZONE_FAULT,
|
||||
SIGNAL_ZONE_RESTORE,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = "alarmdecoder"
|
||||
|
||||
DATA_AD = "alarmdecoder"
|
||||
|
||||
CONF_DEVICE = "device"
|
||||
CONF_DEVICE_BAUD = "baudrate"
|
||||
CONF_DEVICE_PATH = "path"
|
||||
CONF_DEVICE_PORT = "port"
|
||||
CONF_DEVICE_TYPE = "type"
|
||||
CONF_AUTO_BYPASS = "autobypass"
|
||||
CONF_PANEL_DISPLAY = "panel_display"
|
||||
CONF_ZONE_NAME = "name"
|
||||
CONF_ZONE_TYPE = "type"
|
||||
CONF_ZONE_LOOP = "loop"
|
||||
CONF_ZONE_RFID = "rfid"
|
||||
CONF_ZONES = "zones"
|
||||
CONF_RELAY_ADDR = "relayaddr"
|
||||
CONF_RELAY_CHAN = "relaychan"
|
||||
CONF_CODE_ARM_REQUIRED = "code_arm_required"
|
||||
|
||||
DEFAULT_DEVICE_TYPE = "socket"
|
||||
DEFAULT_DEVICE_HOST = "localhost"
|
||||
DEFAULT_DEVICE_PORT = 10000
|
||||
DEFAULT_DEVICE_PATH = "/dev/ttyUSB0"
|
||||
DEFAULT_DEVICE_BAUD = 115200
|
||||
|
||||
DEFAULT_AUTO_BYPASS = False
|
||||
DEFAULT_PANEL_DISPLAY = False
|
||||
DEFAULT_CODE_ARM_REQUIRED = True
|
||||
|
||||
DEFAULT_ZONE_TYPE = "opening"
|
||||
|
||||
SIGNAL_PANEL_MESSAGE = "alarmdecoder.panel_message"
|
||||
SIGNAL_PANEL_ARM_AWAY = "alarmdecoder.panel_arm_away"
|
||||
SIGNAL_PANEL_ARM_HOME = "alarmdecoder.panel_arm_home"
|
||||
SIGNAL_PANEL_DISARM = "alarmdecoder.panel_disarm"
|
||||
|
||||
SIGNAL_ZONE_FAULT = "alarmdecoder.zone_fault"
|
||||
SIGNAL_ZONE_RESTORE = "alarmdecoder.zone_restore"
|
||||
SIGNAL_RFX_MESSAGE = "alarmdecoder.rfx_message"
|
||||
SIGNAL_REL_MESSAGE = "alarmdecoder.rel_message"
|
||||
|
||||
DEVICE_SOCKET_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_DEVICE_TYPE): "socket",
|
||||
vol.Optional(CONF_HOST, default=DEFAULT_DEVICE_HOST): cv.string,
|
||||
vol.Optional(CONF_DEVICE_PORT, default=DEFAULT_DEVICE_PORT): cv.port,
|
||||
}
|
||||
)
|
||||
|
||||
DEVICE_SERIAL_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_DEVICE_TYPE): "serial",
|
||||
vol.Optional(CONF_DEVICE_PATH, default=DEFAULT_DEVICE_PATH): cv.string,
|
||||
vol.Optional(CONF_DEVICE_BAUD, default=DEFAULT_DEVICE_BAUD): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
DEVICE_USB_SCHEMA = vol.Schema({vol.Required(CONF_DEVICE_TYPE): "usb"})
|
||||
|
||||
ZONE_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_ZONE_NAME): cv.string,
|
||||
vol.Optional(CONF_ZONE_TYPE, default=DEFAULT_ZONE_TYPE): vol.Any(
|
||||
DEVICE_CLASSES_SCHEMA
|
||||
),
|
||||
vol.Optional(CONF_ZONE_RFID): cv.string,
|
||||
vol.Optional(CONF_ZONE_LOOP): vol.All(vol.Coerce(int), vol.Range(min=1, max=4)),
|
||||
vol.Inclusive(
|
||||
CONF_RELAY_ADDR,
|
||||
"relaylocation",
|
||||
"Relay address and channel must exist together",
|
||||
): cv.byte,
|
||||
vol.Inclusive(
|
||||
CONF_RELAY_CHAN,
|
||||
"relaylocation",
|
||||
"Relay address and channel must exist together",
|
||||
): cv.byte,
|
||||
}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_DEVICE): vol.Any(
|
||||
DEVICE_SOCKET_SCHEMA, DEVICE_SERIAL_SCHEMA, DEVICE_USB_SCHEMA
|
||||
),
|
||||
vol.Optional(
|
||||
CONF_PANEL_DISPLAY, default=DEFAULT_PANEL_DISPLAY
|
||||
): cv.boolean,
|
||||
vol.Optional(CONF_AUTO_BYPASS, default=DEFAULT_AUTO_BYPASS): cv.boolean,
|
||||
vol.Optional(
|
||||
CONF_CODE_ARM_REQUIRED, default=DEFAULT_CODE_ARM_REQUIRED
|
||||
): cv.boolean,
|
||||
vol.Optional(CONF_ZONES): {vol.Coerce(int): ZONE_SCHEMA},
|
||||
}
|
||||
)
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
PLATFORMS = ["alarm_control_panel", "sensor", "binary_sensor"]
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
async def async_setup(hass, config):
|
||||
"""Set up for the AlarmDecoder devices."""
|
||||
conf = config.get(DOMAIN)
|
||||
return True
|
||||
|
||||
restart = False
|
||||
device = conf[CONF_DEVICE]
|
||||
display = conf[CONF_PANEL_DISPLAY]
|
||||
auto_bypass = conf[CONF_AUTO_BYPASS]
|
||||
code_arm_required = conf[CONF_CODE_ARM_REQUIRED]
|
||||
zones = conf.get(CONF_ZONES)
|
||||
|
||||
device_type = device[CONF_DEVICE_TYPE]
|
||||
host = DEFAULT_DEVICE_HOST
|
||||
port = DEFAULT_DEVICE_PORT
|
||||
path = DEFAULT_DEVICE_PATH
|
||||
baud = DEFAULT_DEVICE_BAUD
|
||||
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool:
|
||||
"""Set up AlarmDecoder config flow."""
|
||||
undo_listener = entry.add_update_listener(_update_listener)
|
||||
|
||||
ad_connection = entry.data
|
||||
protocol = ad_connection[CONF_PROTOCOL]
|
||||
|
||||
def stop_alarmdecoder(event):
|
||||
"""Handle the shutdown of AlarmDecoder."""
|
||||
if not hass.data.get(DOMAIN):
|
||||
return
|
||||
_LOGGER.debug("Shutting down alarmdecoder")
|
||||
nonlocal restart
|
||||
restart = False
|
||||
hass.data[DOMAIN][entry.entry_id][DATA_RESTART] = False
|
||||
controller.close()
|
||||
|
||||
def open_connection(now=None):
|
||||
async def open_connection(now=None):
|
||||
"""Open a connection to AlarmDecoder."""
|
||||
nonlocal restart
|
||||
try:
|
||||
controller.open(baud)
|
||||
await hass.async_add_executor_job(controller.open, baud)
|
||||
except NoDeviceError:
|
||||
_LOGGER.debug("Failed to connect. Retrying in 5 seconds")
|
||||
hass.helpers.event.track_point_in_time(
|
||||
_LOGGER.debug("Failed to connect. Retrying in 5 seconds")
|
||||
hass.helpers.event.async_track_point_in_time(
|
||||
open_connection, dt_util.utcnow() + timedelta(seconds=5)
|
||||
)
|
||||
return
|
||||
_LOGGER.debug("Established a connection with the alarmdecoder")
|
||||
restart = True
|
||||
hass.data[DOMAIN][entry.entry_id][DATA_RESTART] = True
|
||||
|
||||
def handle_closed_connection(event):
|
||||
"""Restart after unexpected loss of connection."""
|
||||
nonlocal restart
|
||||
if not restart:
|
||||
if not hass.data[DOMAIN][entry.entry_id][DATA_RESTART]:
|
||||
return
|
||||
restart = False
|
||||
hass.data[DOMAIN][entry.entry_id][DATA_RESTART] = False
|
||||
_LOGGER.warning("AlarmDecoder unexpectedly lost connection")
|
||||
hass.add_job(open_connection)
|
||||
|
||||
@ -185,18 +100,14 @@ def setup(hass, config):
|
||||
"""Handle relay or zone expander message from AlarmDecoder."""
|
||||
hass.helpers.dispatcher.dispatcher_send(SIGNAL_REL_MESSAGE, message)
|
||||
|
||||
controller = False
|
||||
if device_type == "socket":
|
||||
host = device[CONF_HOST]
|
||||
port = device[CONF_DEVICE_PORT]
|
||||
baud = ad_connection.get(CONF_DEVICE_BAUD)
|
||||
if protocol == PROTOCOL_SOCKET:
|
||||
host = ad_connection[CONF_HOST]
|
||||
port = ad_connection[CONF_PORT]
|
||||
controller = AdExt(SocketDevice(interface=(host, port)))
|
||||
elif device_type == "serial":
|
||||
path = device[CONF_DEVICE_PATH]
|
||||
baud = device[CONF_DEVICE_BAUD]
|
||||
if protocol == PROTOCOL_SERIAL:
|
||||
path = ad_connection[CONF_DEVICE_PATH]
|
||||
controller = AdExt(SerialDevice(interface=path))
|
||||
elif device_type == "usb":
|
||||
AdExt(USBDevice.find())
|
||||
return False
|
||||
|
||||
controller.on_message += handle_message
|
||||
controller.on_rfx_message += handle_rfx_message
|
||||
@ -205,24 +116,56 @@ def setup(hass, config):
|
||||
controller.on_close += handle_closed_connection
|
||||
controller.on_expander_message += handle_rel_message
|
||||
|
||||
hass.data[DATA_AD] = controller
|
||||
|
||||
open_connection()
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_alarmdecoder)
|
||||
|
||||
load_platform(
|
||||
hass,
|
||||
"alarm_control_panel",
|
||||
DOMAIN,
|
||||
{CONF_AUTO_BYPASS: auto_bypass, CONF_CODE_ARM_REQUIRED: code_arm_required},
|
||||
config,
|
||||
remove_stop_listener = hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_STOP, stop_alarmdecoder
|
||||
)
|
||||
|
||||
if zones:
|
||||
load_platform(hass, "binary_sensor", DOMAIN, {CONF_ZONES: zones}, config)
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = {
|
||||
DATA_AD: controller,
|
||||
DATA_REMOVE_UPDATE_LISTENER: undo_listener,
|
||||
DATA_REMOVE_STOP_LISTENER: remove_stop_listener,
|
||||
DATA_RESTART: False,
|
||||
}
|
||||
|
||||
if display:
|
||||
load_platform(hass, "sensor", DOMAIN, conf, config)
|
||||
await open_connection()
|
||||
|
||||
for component in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(entry, component)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry):
|
||||
"""Unload a AlarmDecoder entry."""
|
||||
hass.data[DOMAIN][entry.entry_id][DATA_RESTART] = False
|
||||
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
hass.config_entries.async_forward_entry_unload(entry, component)
|
||||
for component in PLATFORMS
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
if not unload_ok:
|
||||
return False
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id][DATA_REMOVE_UPDATE_LISTENER]()
|
||||
hass.data[DOMAIN][entry.entry_id][DATA_REMOVE_STOP_LISTENER]()
|
||||
await hass.async_add_executor_job(hass.data[DOMAIN][entry.entry_id][DATA_AD].close)
|
||||
|
||||
if hass.data[DOMAIN][entry.entry_id]:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
if not hass.data[DOMAIN]:
|
||||
hass.data.pop(DOMAIN)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def _update_listener(hass: HomeAssistantType, entry: ConfigEntry):
|
||||
"""Handle options update."""
|
||||
_LOGGER.debug("AlarmDecoder options updated: %s", entry.as_dict()["options"])
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
@ -12,6 +12,7 @@ from homeassistant.components.alarm_control_panel.const import (
|
||||
SUPPORT_ALARM_ARM_HOME,
|
||||
SUPPORT_ALARM_ARM_NIGHT,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_CODE,
|
||||
STATE_ALARM_ARMED_AWAY,
|
||||
@ -20,66 +21,70 @@ from homeassistant.const import (
|
||||
STATE_ALARM_DISARMED,
|
||||
STATE_ALARM_TRIGGERED,
|
||||
)
|
||||
from homeassistant.helpers import entity_platform
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
from . import (
|
||||
from .const import (
|
||||
CONF_ALT_NIGHT_MODE,
|
||||
CONF_AUTO_BYPASS,
|
||||
CONF_CODE_ARM_REQUIRED,
|
||||
DATA_AD,
|
||||
DEFAULT_ARM_OPTIONS,
|
||||
DOMAIN,
|
||||
OPTIONS_ARM,
|
||||
SIGNAL_PANEL_MESSAGE,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SERVICE_ALARM_TOGGLE_CHIME = "alarm_toggle_chime"
|
||||
ALARM_TOGGLE_CHIME_SCHEMA = vol.Schema({vol.Required(ATTR_CODE): cv.string})
|
||||
|
||||
SERVICE_ALARM_KEYPRESS = "alarm_keypress"
|
||||
ATTR_KEYPRESS = "keypress"
|
||||
ALARM_KEYPRESS_SCHEMA = vol.Schema({vol.Required(ATTR_KEYPRESS): cv.string})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
|
||||
):
|
||||
"""Set up for AlarmDecoder alarm panels."""
|
||||
if discovery_info is None:
|
||||
return
|
||||
options = entry.options
|
||||
arm_options = options.get(OPTIONS_ARM, DEFAULT_ARM_OPTIONS)
|
||||
client = hass.data[DOMAIN][entry.entry_id][DATA_AD]
|
||||
|
||||
auto_bypass = discovery_info[CONF_AUTO_BYPASS]
|
||||
code_arm_required = discovery_info[CONF_CODE_ARM_REQUIRED]
|
||||
entity = AlarmDecoderAlarmPanel(auto_bypass, code_arm_required)
|
||||
add_entities([entity])
|
||||
entity = AlarmDecoderAlarmPanel(
|
||||
client=client,
|
||||
auto_bypass=arm_options[CONF_AUTO_BYPASS],
|
||||
code_arm_required=arm_options[CONF_CODE_ARM_REQUIRED],
|
||||
alt_night_mode=arm_options[CONF_ALT_NIGHT_MODE],
|
||||
)
|
||||
async_add_entities([entity])
|
||||
|
||||
def alarm_toggle_chime_handler(service):
|
||||
"""Register toggle chime handler."""
|
||||
code = service.data.get(ATTR_CODE)
|
||||
entity.alarm_toggle_chime(code)
|
||||
platform = entity_platform.current_platform.get()
|
||||
|
||||
hass.services.register(
|
||||
DOMAIN,
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_ALARM_TOGGLE_CHIME,
|
||||
alarm_toggle_chime_handler,
|
||||
schema=ALARM_TOGGLE_CHIME_SCHEMA,
|
||||
{
|
||||
vol.Required(ATTR_CODE): cv.string,
|
||||
},
|
||||
"alarm_toggle_chime",
|
||||
)
|
||||
|
||||
def alarm_keypress_handler(service):
|
||||
"""Register keypress handler."""
|
||||
keypress = service.data[ATTR_KEYPRESS]
|
||||
entity.alarm_keypress(keypress)
|
||||
|
||||
hass.services.register(
|
||||
DOMAIN,
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_ALARM_KEYPRESS,
|
||||
alarm_keypress_handler,
|
||||
schema=ALARM_KEYPRESS_SCHEMA,
|
||||
{
|
||||
vol.Required(ATTR_KEYPRESS): cv.string,
|
||||
},
|
||||
"alarm_keypress",
|
||||
)
|
||||
|
||||
|
||||
class AlarmDecoderAlarmPanel(AlarmControlPanelEntity):
|
||||
"""Representation of an AlarmDecoder-based alarm panel."""
|
||||
|
||||
def __init__(self, auto_bypass, code_arm_required):
|
||||
def __init__(self, client, auto_bypass, code_arm_required, alt_night_mode):
|
||||
"""Initialize the alarm panel."""
|
||||
self._client = client
|
||||
self._display = ""
|
||||
self._name = "Alarm Panel"
|
||||
self._state = None
|
||||
@ -95,6 +100,7 @@ class AlarmDecoderAlarmPanel(AlarmControlPanelEntity):
|
||||
self._zone_bypassed = None
|
||||
self._auto_bypass = auto_bypass
|
||||
self._code_arm_required = code_arm_required
|
||||
self._alt_night_mode = alt_night_mode
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
@ -180,11 +186,11 @@ class AlarmDecoderAlarmPanel(AlarmControlPanelEntity):
|
||||
def alarm_disarm(self, code=None):
|
||||
"""Send disarm command."""
|
||||
if code:
|
||||
self.hass.data[DATA_AD].send(f"{code!s}1")
|
||||
self._client.send(f"{code!s}1")
|
||||
|
||||
def alarm_arm_away(self, code=None):
|
||||
"""Send arm away command."""
|
||||
self.hass.data[DATA_AD].arm_away(
|
||||
self._client.arm_away(
|
||||
code=code,
|
||||
code_arm_required=self._code_arm_required,
|
||||
auto_bypass=self._auto_bypass,
|
||||
@ -192,7 +198,7 @@ class AlarmDecoderAlarmPanel(AlarmControlPanelEntity):
|
||||
|
||||
def alarm_arm_home(self, code=None):
|
||||
"""Send arm home command."""
|
||||
self.hass.data[DATA_AD].arm_home(
|
||||
self._client.arm_home(
|
||||
code=code,
|
||||
code_arm_required=self._code_arm_required,
|
||||
auto_bypass=self._auto_bypass,
|
||||
@ -200,18 +206,19 @@ class AlarmDecoderAlarmPanel(AlarmControlPanelEntity):
|
||||
|
||||
def alarm_arm_night(self, code=None):
|
||||
"""Send arm night command."""
|
||||
self.hass.data[DATA_AD].arm_night(
|
||||
self._client.arm_night(
|
||||
code=code,
|
||||
code_arm_required=self._code_arm_required,
|
||||
alt_night_mode=self._alt_night_mode,
|
||||
auto_bypass=self._auto_bypass,
|
||||
)
|
||||
|
||||
def alarm_toggle_chime(self, code=None):
|
||||
"""Send toggle chime command."""
|
||||
if code:
|
||||
self.hass.data[DATA_AD].send(f"{code!s}9")
|
||||
self._client.send(f"{code!s}9")
|
||||
|
||||
def alarm_keypress(self, keypress):
|
||||
"""Send custom keypresses."""
|
||||
if keypress:
|
||||
self.hass.data[DATA_AD].send(keypress)
|
||||
self._client.send(keypress)
|
||||
|
@ -2,20 +2,23 @@
|
||||
import logging
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
from . import (
|
||||
from .const import (
|
||||
CONF_RELAY_ADDR,
|
||||
CONF_RELAY_CHAN,
|
||||
CONF_ZONE_LOOP,
|
||||
CONF_ZONE_NAME,
|
||||
CONF_ZONE_NUMBER,
|
||||
CONF_ZONE_RFID,
|
||||
CONF_ZONE_TYPE,
|
||||
CONF_ZONES,
|
||||
DEFAULT_ZONE_OPTIONS,
|
||||
OPTIONS_ZONES,
|
||||
SIGNAL_REL_MESSAGE,
|
||||
SIGNAL_RFX_MESSAGE,
|
||||
SIGNAL_ZONE_FAULT,
|
||||
SIGNAL_ZONE_RESTORE,
|
||||
ZONE_SCHEMA,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -30,27 +33,28 @@ ATTR_RF_LOOP4 = "rf_loop4"
|
||||
ATTR_RF_LOOP1 = "rf_loop1"
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the AlarmDecoder binary sensor devices."""
|
||||
configured_zones = discovery_info[CONF_ZONES]
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
|
||||
):
|
||||
"""Set up for AlarmDecoder sensor."""
|
||||
|
||||
devices = []
|
||||
for zone_num in configured_zones:
|
||||
device_config_data = ZONE_SCHEMA(configured_zones[zone_num])
|
||||
zone_type = device_config_data[CONF_ZONE_TYPE]
|
||||
zone_name = device_config_data[CONF_ZONE_NAME]
|
||||
zone_rfid = device_config_data.get(CONF_ZONE_RFID)
|
||||
zone_loop = device_config_data.get(CONF_ZONE_LOOP)
|
||||
relay_addr = device_config_data.get(CONF_RELAY_ADDR)
|
||||
relay_chan = device_config_data.get(CONF_RELAY_CHAN)
|
||||
device = AlarmDecoderBinarySensor(
|
||||
zones = entry.options.get(OPTIONS_ZONES, DEFAULT_ZONE_OPTIONS)
|
||||
|
||||
entities = []
|
||||
for zone_num in zones:
|
||||
zone_info = zones[zone_num]
|
||||
zone_type = zone_info[CONF_ZONE_TYPE]
|
||||
zone_name = zone_info[CONF_ZONE_NAME]
|
||||
zone_rfid = zone_info.get(CONF_ZONE_RFID)
|
||||
zone_loop = zone_info.get(CONF_ZONE_LOOP)
|
||||
relay_addr = zone_info.get(CONF_RELAY_ADDR)
|
||||
relay_chan = zone_info.get(CONF_RELAY_CHAN)
|
||||
entity = AlarmDecoderBinarySensor(
|
||||
zone_num, zone_name, zone_type, zone_rfid, zone_loop, relay_addr, relay_chan
|
||||
)
|
||||
devices.append(device)
|
||||
entities.append(entity)
|
||||
|
||||
add_entities(devices)
|
||||
|
||||
return True
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class AlarmDecoderBinarySensor(BinarySensorEntity):
|
||||
@ -67,7 +71,7 @@ class AlarmDecoderBinarySensor(BinarySensorEntity):
|
||||
relay_chan,
|
||||
):
|
||||
"""Initialize the binary_sensor."""
|
||||
self._zone_number = zone_number
|
||||
self._zone_number = int(zone_number)
|
||||
self._zone_type = zone_type
|
||||
self._state = None
|
||||
self._name = zone_name
|
||||
@ -117,6 +121,7 @@ class AlarmDecoderBinarySensor(BinarySensorEntity):
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
attr = {}
|
||||
attr[CONF_ZONE_NUMBER] = self._zone_number
|
||||
if self._rfid and self._rfstate is not None:
|
||||
attr[ATTR_RF_BIT0] = bool(self._rfstate & 0x01)
|
||||
attr[ATTR_RF_LOW_BAT] = bool(self._rfstate & 0x02)
|
||||
|
360
homeassistant/components/alarmdecoder/config_flow.py
Normal file
360
homeassistant/components/alarmdecoder/config_flow.py
Normal file
@ -0,0 +1,360 @@
|
||||
"""Config flow for AlarmDecoder."""
|
||||
import logging
|
||||
|
||||
from adext import AdExt
|
||||
from alarmdecoder.devices import SerialDevice, SocketDevice
|
||||
from alarmdecoder.util import NoDeviceError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.binary_sensor import DEVICE_CLASSES
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_PROTOCOL
|
||||
from homeassistant.core import callback
|
||||
|
||||
from .const import ( # pylint: disable=unused-import
|
||||
CONF_ALT_NIGHT_MODE,
|
||||
CONF_AUTO_BYPASS,
|
||||
CONF_CODE_ARM_REQUIRED,
|
||||
CONF_DEVICE_BAUD,
|
||||
CONF_DEVICE_PATH,
|
||||
CONF_RELAY_ADDR,
|
||||
CONF_RELAY_CHAN,
|
||||
CONF_ZONE_LOOP,
|
||||
CONF_ZONE_NAME,
|
||||
CONF_ZONE_NUMBER,
|
||||
CONF_ZONE_RFID,
|
||||
CONF_ZONE_TYPE,
|
||||
DEFAULT_ARM_OPTIONS,
|
||||
DEFAULT_DEVICE_BAUD,
|
||||
DEFAULT_DEVICE_HOST,
|
||||
DEFAULT_DEVICE_PATH,
|
||||
DEFAULT_DEVICE_PORT,
|
||||
DEFAULT_ZONE_OPTIONS,
|
||||
DEFAULT_ZONE_TYPE,
|
||||
DOMAIN,
|
||||
OPTIONS_ARM,
|
||||
OPTIONS_ZONES,
|
||||
PROTOCOL_SERIAL,
|
||||
PROTOCOL_SOCKET,
|
||||
)
|
||||
|
||||
EDIT_KEY = "edit_selection"
|
||||
EDIT_ZONES = "Zones"
|
||||
EDIT_SETTINGS = "Arming Settings"
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AlarmDecoderFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a AlarmDecoder config flow."""
|
||||
|
||||
VERSION = 1
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize AlarmDecoder ConfigFlow."""
|
||||
self.protocol = None
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry):
|
||||
"""Get the options flow for AlarmDecoder."""
|
||||
return AlarmDecoderOptionsFlowHandler(config_entry)
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle a flow initialized by the user."""
|
||||
if user_input is not None:
|
||||
self.protocol = user_input[CONF_PROTOCOL]
|
||||
return await self.async_step_protocol()
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_PROTOCOL): vol.In(
|
||||
[PROTOCOL_SOCKET, PROTOCOL_SERIAL]
|
||||
),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
async def async_step_protocol(self, user_input=None):
|
||||
"""Handle AlarmDecoder protocol setup."""
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
if _device_already_added(
|
||||
self._async_current_entries(), user_input, self.protocol
|
||||
):
|
||||
return self.async_abort(reason="already_configured")
|
||||
connection = {}
|
||||
baud = None
|
||||
if self.protocol == PROTOCOL_SOCKET:
|
||||
host = connection[CONF_HOST] = user_input[CONF_HOST]
|
||||
port = connection[CONF_PORT] = user_input[CONF_PORT]
|
||||
title = f"{host}:{port}"
|
||||
device = SocketDevice(interface=(host, port))
|
||||
if self.protocol == PROTOCOL_SERIAL:
|
||||
path = connection[CONF_DEVICE_PATH] = user_input[CONF_DEVICE_PATH]
|
||||
baud = connection[CONF_DEVICE_BAUD] = user_input[CONF_DEVICE_BAUD]
|
||||
title = path
|
||||
device = SerialDevice(interface=path)
|
||||
|
||||
controller = AdExt(device)
|
||||
|
||||
def test_connection():
|
||||
controller.open(baud)
|
||||
controller.close()
|
||||
|
||||
try:
|
||||
await self.hass.async_add_executor_job(test_connection)
|
||||
return self.async_create_entry(
|
||||
title=title, data={CONF_PROTOCOL: self.protocol, **connection}
|
||||
)
|
||||
except NoDeviceError:
|
||||
errors["base"] = "service_unavailable"
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception("Unexpected exception during AlarmDecoder setup")
|
||||
errors["base"] = "unknown"
|
||||
|
||||
if self.protocol == PROTOCOL_SOCKET:
|
||||
schema = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_HOST, default=DEFAULT_DEVICE_HOST): str,
|
||||
vol.Required(CONF_PORT, default=DEFAULT_DEVICE_PORT): int,
|
||||
}
|
||||
)
|
||||
if self.protocol == PROTOCOL_SERIAL:
|
||||
schema = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_DEVICE_PATH, default=DEFAULT_DEVICE_PATH): str,
|
||||
vol.Required(CONF_DEVICE_BAUD, default=DEFAULT_DEVICE_BAUD): int,
|
||||
}
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="protocol",
|
||||
data_schema=schema,
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
|
||||
class AlarmDecoderOptionsFlowHandler(config_entries.OptionsFlow):
|
||||
"""Handle AlarmDecoder options."""
|
||||
|
||||
def __init__(self, config_entry: config_entries.ConfigEntry):
|
||||
"""Initialize AlarmDecoder options flow."""
|
||||
self.arm_options = config_entry.options.get(OPTIONS_ARM, DEFAULT_ARM_OPTIONS)
|
||||
self.zone_options = config_entry.options.get(
|
||||
OPTIONS_ZONES, DEFAULT_ZONE_OPTIONS
|
||||
)
|
||||
self.selected_zone = None
|
||||
|
||||
async def async_step_init(self, user_input=None):
|
||||
"""Manage the options."""
|
||||
if user_input is not None:
|
||||
if user_input[EDIT_KEY] == EDIT_SETTINGS:
|
||||
return await self.async_step_arm_settings()
|
||||
if user_input[EDIT_KEY] == EDIT_ZONES:
|
||||
return await self.async_step_zone_select()
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="init",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(EDIT_KEY, default=EDIT_SETTINGS): vol.In(
|
||||
[EDIT_SETTINGS, EDIT_ZONES]
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
async def async_step_arm_settings(self, user_input=None):
|
||||
"""Arming options form."""
|
||||
if user_input is not None:
|
||||
return self.async_create_entry(
|
||||
title="",
|
||||
data={OPTIONS_ARM: user_input, OPTIONS_ZONES: self.zone_options},
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="arm_settings",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_ALT_NIGHT_MODE,
|
||||
default=self.arm_options[CONF_ALT_NIGHT_MODE],
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_AUTO_BYPASS, default=self.arm_options[CONF_AUTO_BYPASS]
|
||||
): bool,
|
||||
vol.Optional(
|
||||
CONF_CODE_ARM_REQUIRED,
|
||||
default=self.arm_options[CONF_CODE_ARM_REQUIRED],
|
||||
): bool,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
async def async_step_zone_select(self, user_input=None):
|
||||
"""Zone selection form."""
|
||||
errors = _validate_zone_input(user_input)
|
||||
|
||||
if user_input is not None and not errors:
|
||||
self.selected_zone = str(
|
||||
int(user_input[CONF_ZONE_NUMBER])
|
||||
) # remove leading zeros
|
||||
return await self.async_step_zone_details()
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="zone_select",
|
||||
data_schema=vol.Schema({vol.Required(CONF_ZONE_NUMBER): str}),
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_zone_details(self, user_input=None):
|
||||
"""Zone details form."""
|
||||
errors = _validate_zone_input(user_input)
|
||||
|
||||
if user_input is not None and not errors:
|
||||
zone_options = self.zone_options.copy()
|
||||
zone_id = self.selected_zone
|
||||
zone_options[zone_id] = _fix_input_types(user_input)
|
||||
|
||||
# Delete zone entry if zone_name is omitted
|
||||
if CONF_ZONE_NAME not in zone_options[zone_id]:
|
||||
zone_options.pop(zone_id)
|
||||
|
||||
return self.async_create_entry(
|
||||
title="",
|
||||
data={OPTIONS_ARM: self.arm_options, OPTIONS_ZONES: zone_options},
|
||||
)
|
||||
|
||||
existing_zone_settings = self.zone_options.get(self.selected_zone, {})
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="zone_details",
|
||||
description_placeholders={CONF_ZONE_NUMBER: self.selected_zone},
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_ZONE_NAME,
|
||||
description={
|
||||
"suggested_value": existing_zone_settings.get(
|
||||
CONF_ZONE_NAME
|
||||
)
|
||||
},
|
||||
): str,
|
||||
vol.Optional(
|
||||
CONF_ZONE_TYPE,
|
||||
default=existing_zone_settings.get(
|
||||
CONF_ZONE_TYPE, DEFAULT_ZONE_TYPE
|
||||
),
|
||||
): vol.In(DEVICE_CLASSES),
|
||||
vol.Optional(
|
||||
CONF_ZONE_RFID,
|
||||
description={
|
||||
"suggested_value": existing_zone_settings.get(
|
||||
CONF_ZONE_RFID
|
||||
)
|
||||
},
|
||||
): str,
|
||||
vol.Optional(
|
||||
CONF_ZONE_LOOP,
|
||||
description={
|
||||
"suggested_value": existing_zone_settings.get(
|
||||
CONF_ZONE_LOOP
|
||||
)
|
||||
},
|
||||
): str,
|
||||
vol.Optional(
|
||||
CONF_RELAY_ADDR,
|
||||
description={
|
||||
"suggested_value": existing_zone_settings.get(
|
||||
CONF_RELAY_ADDR
|
||||
)
|
||||
},
|
||||
): str,
|
||||
vol.Optional(
|
||||
CONF_RELAY_CHAN,
|
||||
description={
|
||||
"suggested_value": existing_zone_settings.get(
|
||||
CONF_RELAY_CHAN
|
||||
)
|
||||
},
|
||||
): str,
|
||||
}
|
||||
),
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
|
||||
def _validate_zone_input(zone_input):
|
||||
if not zone_input:
|
||||
return {}
|
||||
errors = {}
|
||||
|
||||
# CONF_RELAY_ADDR & CONF_RELAY_CHAN are inclusive
|
||||
if (CONF_RELAY_ADDR in zone_input and CONF_RELAY_CHAN not in zone_input) or (
|
||||
CONF_RELAY_ADDR not in zone_input and CONF_RELAY_CHAN in zone_input
|
||||
):
|
||||
errors["base"] = "relay_inclusive"
|
||||
|
||||
# The following keys must be int
|
||||
for key in [CONF_ZONE_NUMBER, CONF_ZONE_LOOP, CONF_RELAY_ADDR, CONF_RELAY_CHAN]:
|
||||
if key in zone_input:
|
||||
try:
|
||||
int(zone_input[key])
|
||||
except ValueError:
|
||||
errors[key] = "int"
|
||||
|
||||
# CONF_ZONE_LOOP depends on CONF_ZONE_RFID
|
||||
if CONF_ZONE_LOOP in zone_input and CONF_ZONE_RFID not in zone_input:
|
||||
errors[CONF_ZONE_LOOP] = "loop_rfid"
|
||||
|
||||
# CONF_ZONE_LOOP must be 1-4
|
||||
if (
|
||||
CONF_ZONE_LOOP in zone_input
|
||||
and zone_input[CONF_ZONE_LOOP].isdigit()
|
||||
and int(zone_input[CONF_ZONE_LOOP]) not in list(range(1, 5))
|
||||
):
|
||||
errors[CONF_ZONE_LOOP] = "loop_range"
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def _fix_input_types(zone_input):
|
||||
"""Convert necessary keys to int.
|
||||
|
||||
Since ConfigFlow inputs of type int cannot default to an empty string, we collect the values below as
|
||||
strings and then convert them to ints.
|
||||
"""
|
||||
|
||||
for key in [CONF_ZONE_LOOP, CONF_RELAY_ADDR, CONF_RELAY_CHAN]:
|
||||
if key in zone_input:
|
||||
zone_input[key] = int(zone_input[key])
|
||||
|
||||
return zone_input
|
||||
|
||||
|
||||
def _device_already_added(current_entries, user_input, protocol):
|
||||
"""Determine if entry has already been added to HA."""
|
||||
user_host = user_input.get(CONF_HOST)
|
||||
user_port = user_input.get(CONF_PORT)
|
||||
user_path = user_input.get(CONF_DEVICE_PATH)
|
||||
user_baud = user_input.get(CONF_DEVICE_BAUD)
|
||||
|
||||
for entry in current_entries:
|
||||
entry_host = entry.data.get(CONF_HOST)
|
||||
entry_port = entry.data.get(CONF_PORT)
|
||||
entry_path = entry.data.get(CONF_DEVICE_PATH)
|
||||
entry_baud = entry.data.get(CONF_DEVICE_BAUD)
|
||||
|
||||
if protocol == PROTOCOL_SOCKET:
|
||||
if user_host == entry_host and user_port == entry_port:
|
||||
return True
|
||||
|
||||
if protocol == PROTOCOL_SERIAL:
|
||||
if user_baud == entry_baud and user_path == entry_path:
|
||||
return True
|
||||
|
||||
return False
|
49
homeassistant/components/alarmdecoder/const.py
Normal file
49
homeassistant/components/alarmdecoder/const.py
Normal file
@ -0,0 +1,49 @@
|
||||
"""Constants for the AlarmDecoder component."""
|
||||
|
||||
CONF_ALT_NIGHT_MODE = "alt_night_mode"
|
||||
CONF_AUTO_BYPASS = "auto_bypass"
|
||||
CONF_CODE_ARM_REQUIRED = "code_arm_required"
|
||||
CONF_DEVICE_BAUD = "device_baudrate"
|
||||
CONF_DEVICE_PATH = "device_path"
|
||||
CONF_RELAY_ADDR = "zone_relayaddr"
|
||||
CONF_RELAY_CHAN = "zone_relaychan"
|
||||
CONF_ZONE_LOOP = "zone_loop"
|
||||
CONF_ZONE_NAME = "zone_name"
|
||||
CONF_ZONE_NUMBER = "zone_number"
|
||||
CONF_ZONE_RFID = "zone_rfid"
|
||||
CONF_ZONE_TYPE = "zone_type"
|
||||
|
||||
DATA_AD = "alarmdecoder"
|
||||
DATA_REMOVE_STOP_LISTENER = "rm_stop_listener"
|
||||
DATA_REMOVE_UPDATE_LISTENER = "rm_update_listener"
|
||||
DATA_RESTART = "restart"
|
||||
|
||||
DEFAULT_ALT_NIGHT_MODE = False
|
||||
DEFAULT_AUTO_BYPASS = False
|
||||
DEFAULT_CODE_ARM_REQUIRED = True
|
||||
DEFAULT_DEVICE_BAUD = 115200
|
||||
DEFAULT_DEVICE_HOST = "alarmdecoder"
|
||||
DEFAULT_DEVICE_PATH = "/dev/ttyUSB0"
|
||||
DEFAULT_DEVICE_PORT = 10000
|
||||
DEFAULT_ZONE_TYPE = "window"
|
||||
|
||||
DEFAULT_ARM_OPTIONS = {
|
||||
CONF_ALT_NIGHT_MODE: DEFAULT_ALT_NIGHT_MODE,
|
||||
CONF_AUTO_BYPASS: DEFAULT_AUTO_BYPASS,
|
||||
CONF_CODE_ARM_REQUIRED: DEFAULT_CODE_ARM_REQUIRED,
|
||||
}
|
||||
DEFAULT_ZONE_OPTIONS = {}
|
||||
|
||||
DOMAIN = "alarmdecoder"
|
||||
|
||||
OPTIONS_ARM = "arm_options"
|
||||
OPTIONS_ZONES = "zone_options"
|
||||
|
||||
PROTOCOL_SERIAL = "serial"
|
||||
PROTOCOL_SOCKET = "socket"
|
||||
|
||||
SIGNAL_PANEL_MESSAGE = "alarmdecoder.panel_message"
|
||||
SIGNAL_REL_MESSAGE = "alarmdecoder.rel_message"
|
||||
SIGNAL_RFX_MESSAGE = "alarmdecoder.rfx_message"
|
||||
SIGNAL_ZONE_FAULT = "alarmdecoder.zone_fault"
|
||||
SIGNAL_ZONE_RESTORE = "alarmdecoder.zone_restore"
|
@ -3,5 +3,6 @@
|
||||
"name": "AlarmDecoder",
|
||||
"documentation": "https://www.home-assistant.io/integrations/alarmdecoder",
|
||||
"requirements": ["adext==0.3"],
|
||||
"codeowners": ["@ajschmidt8"]
|
||||
"codeowners": ["@ajschmidt8"],
|
||||
"config_flow": true
|
||||
}
|
||||
|
@ -1,26 +1,29 @@
|
||||
"""Support for AlarmDecoder sensors (Shows Panel Display)."""
|
||||
import logging
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
from . import SIGNAL_PANEL_MESSAGE
|
||||
from .const import SIGNAL_PANEL_MESSAGE
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up for AlarmDecoder sensor devices."""
|
||||
_LOGGER.debug("AlarmDecoderSensor: setup_platform")
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
|
||||
):
|
||||
"""Set up for AlarmDecoder sensor."""
|
||||
|
||||
device = AlarmDecoderSensor(hass)
|
||||
|
||||
add_entities([device])
|
||||
entity = AlarmDecoderSensor()
|
||||
async_add_entities([entity])
|
||||
return True
|
||||
|
||||
|
||||
class AlarmDecoderSensor(Entity):
|
||||
"""Representation of an AlarmDecoder keypad."""
|
||||
|
||||
def __init__(self, hass):
|
||||
def __init__(self):
|
||||
"""Initialize the alarm panel."""
|
||||
self._display = ""
|
||||
self._state = None
|
||||
|
@ -1,6 +1,9 @@
|
||||
alarm_keypress:
|
||||
description: Send custom keypresses to the alarm.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name of alarm control panel to deliver keypress.
|
||||
example: "alarm_control_panel.main"
|
||||
keypress:
|
||||
description: "String to send to the alarm panel."
|
||||
example: "*71"
|
||||
@ -8,6 +11,9 @@ alarm_keypress:
|
||||
alarm_toggle_chime:
|
||||
description: Send the alarm the toggle chime command.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name of alarm control panel to toggle chime.
|
||||
example: "alarm_control_panel.main"
|
||||
code:
|
||||
description: A required code to toggle the alarm control panel chime with.
|
||||
example: 1234
|
||||
|
72
homeassistant/components/alarmdecoder/strings.json
Normal file
72
homeassistant/components/alarmdecoder/strings.json
Normal file
@ -0,0 +1,72 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Choose AlarmDecoder Protocol",
|
||||
"data": {
|
||||
"protocol": "Protocol"
|
||||
}
|
||||
},
|
||||
"protocol": {
|
||||
"title": "Configure connection settings",
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"port": "[%key:common::config_flow::data::port%]",
|
||||
"device_baudrate": "Device Baud Rate",
|
||||
"device_path": "Device Path"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"service_unavailable": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"create_entry": { "default": "Successfully connected to AlarmDecoder." },
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "Configure AlarmDecoder",
|
||||
"description": "What would you like to edit?",
|
||||
"data": {
|
||||
"edit_select": "Edit"
|
||||
}
|
||||
},
|
||||
"arm_settings": {
|
||||
"title": "Configure AlarmDecoder",
|
||||
"data": {
|
||||
"auto_bypass": "Auto Bypass on Arm",
|
||||
"code_arm_required": "Code Required for Arming",
|
||||
"alt_night_mode": "Alternative Night Mode"
|
||||
}
|
||||
},
|
||||
"zone_select": {
|
||||
"title": "Configure AlarmDecoder",
|
||||
"description": "Enter the zone number you'd like to to add, edit, or remove.",
|
||||
"data": {
|
||||
"zone_number": "Zone Number"
|
||||
}
|
||||
},
|
||||
"zone_details": {
|
||||
"title": "Configure AlarmDecoder",
|
||||
"description": "Enter details for zone {zone_number}. To delete zone {zone_number}, leave Zone Name blank.",
|
||||
"data": {
|
||||
"zone_name": "Zone Name",
|
||||
"zone_type": "Zone Type",
|
||||
"zone_rfid": "RF Serial",
|
||||
"zone_loop": "RF Loop",
|
||||
"zone_relayaddr": "Relay Address",
|
||||
"zone_relaychan": "Relay Channel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"relay_inclusive": "Relay Address and Relay Channel are codependent and must be included together.",
|
||||
"int": "The field below must be an integer.",
|
||||
"loop_rfid": "RF Loop cannot be used without RF Serial.",
|
||||
"loop_range": "RF Loop must be an integer between 1 and 4."
|
||||
}
|
||||
}
|
||||
}
|
74
homeassistant/components/alarmdecoder/translations/ca.json
Normal file
74
homeassistant/components/alarmdecoder/translations/ca.json
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "El dispositiu ja est\u00e0 configurat"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "S'ha connectat correctament amb AlarmDecoder."
|
||||
},
|
||||
"error": {
|
||||
"service_unavailable": "Ha fallat la connexi\u00f3"
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"device_baudrate": "Velocitat, en baudis, del dispositiu",
|
||||
"device_path": "Ruta del dispositiu",
|
||||
"host": "Amfitri\u00f3",
|
||||
"port": "Port"
|
||||
},
|
||||
"title": "Configuraci\u00f3 dels par\u00e0metres de connexi\u00f3"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "Protocol"
|
||||
},
|
||||
"title": "Selecciona el protocol d'AlarmDecoder"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"int": "El camp seg\u00fcent ha de ser un nombre enter.",
|
||||
"loop_range": "El bucle RF ha de ser un nombre enter entre 1 i 4.",
|
||||
"loop_rfid": "El bucle RF no es pot utilitzar sense RF s\u00e8rie.",
|
||||
"relay_inclusive": "L'adre\u00e7a i el canal de rel\u00e9 s\u00f3n codependents i s'han d'incloure junts."
|
||||
},
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"data": {
|
||||
"alt_night_mode": "Mode nocturn alternatiu",
|
||||
"auto_bypass": "Bypass autom\u00e0tic en l'activaci\u00f3",
|
||||
"code_arm_required": "Codi necessari per a l'activaci\u00f3"
|
||||
},
|
||||
"title": "Configuraci\u00f3 d'AlarmDecoder"
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "Edita"
|
||||
},
|
||||
"description": "Qu\u00e8 voldries editar?",
|
||||
"title": "Configuraci\u00f3 d'AlarmDecoder"
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_loop": "Bucle RF",
|
||||
"zone_name": "Nom de la zona",
|
||||
"zone_relayaddr": "Adre\u00e7a del rel\u00e9",
|
||||
"zone_relaychan": "Canal del rel\u00e9",
|
||||
"zone_rfid": "RF s\u00e8rie",
|
||||
"zone_type": "Tipus de zona"
|
||||
},
|
||||
"description": "Introdueix els detalls de la zona {zone_number}. Per suprimir la zona {zone_number}, deixa el nom de la zona en blanc.",
|
||||
"title": "Configuraci\u00f3 d'AlarmDecoder"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "N\u00famero de zona"
|
||||
},
|
||||
"description": "Introdueix el n\u00famero de zona que vulguis afegir, editar o eliminar.",
|
||||
"title": "Configuraci\u00f3 d'AlarmDecoder"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
homeassistant/components/alarmdecoder/translations/cs.json
Normal file
35
homeassistant/components/alarmdecoder/translations/cs.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"options": {
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"title": "Konfigurovat AlarmDecoder"
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "Upravit"
|
||||
},
|
||||
"description": "Co chcete upravit?",
|
||||
"title": "Konfigurovat AlarmDecoder"
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_loop": "RF Loop",
|
||||
"zone_name": "N\u00e1zev z\u00f3ny",
|
||||
"zone_relayaddr": "Relay adresa",
|
||||
"zone_relaychan": "Relay kan\u00e1l",
|
||||
"zone_rfid": "RF Serial",
|
||||
"zone_type": "Typ z\u00f3ny"
|
||||
},
|
||||
"description": "Zadejte podrobnosti pro z\u00f3nu {zone_number}. Chcete-li odstranit z\u00f3nu {zone_number}, ponechejte n\u00e1zev z\u00f3ny pr\u00e1zdn\u00fd.",
|
||||
"title": "Konfigurovat AlarmDecoder"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "\u010c\u00edslo z\u00f3ny"
|
||||
},
|
||||
"description": "Zadejte \u010d\u00edslo z\u00f3ny, kterou chcete p\u0159idat, upravit nebo odstranit.",
|
||||
"title": "Konfigurovat AlarmDecoder"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
48
homeassistant/components/alarmdecoder/translations/de.json
Normal file
48
homeassistant/components/alarmdecoder/translations/de.json
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"service_unavailable": "Verbindung konnte nicht hergestellt werden"
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"host": "Host",
|
||||
"port": "Port"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "Protokoll"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"data": {
|
||||
"alt_night_mode": "Alternativer Nachtmodus"
|
||||
}
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "Bearbeiten"
|
||||
},
|
||||
"description": "Was m\u00f6chtest du bearbeiten?"
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_name": "Zonenname",
|
||||
"zone_relayaddr": "Relais-Adresse",
|
||||
"zone_type": "Zonentyp"
|
||||
}
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "Zonennummer"
|
||||
},
|
||||
"description": "Geben Sie die Zonennummer ein, die Sie hinzuf\u00fcgen, bearbeiten oder entfernen m\u00f6chten."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
homeassistant/components/alarmdecoder/translations/el.json
Normal file
34
homeassistant/components/alarmdecoder/translations/el.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"config": {
|
||||
"create_entry": {
|
||||
"default": "\u0395\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7 \u03c3\u03c4\u03bf AlarmDecoder."
|
||||
},
|
||||
"error": {
|
||||
"service_unavailable": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2"
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"device_baudrate": "\u03a1\u03c5\u03b8\u03bc\u03cc\u03c2 Baud \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2",
|
||||
"device_path": "\u0394\u03b9\u03b1\u03b4\u03c1\u03bf\u03bc\u03ae \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae\u03c2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_type": "\u03a4\u03cd\u03c0\u03bf\u03c2 \u03b6\u03ce\u03bd\u03b7\u03c2"
|
||||
},
|
||||
"title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 AlarmDecoder"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "\u0391\u03c1\u03b9\u03b8\u03bc\u03cc\u03c2 \u03b6\u03ce\u03bd\u03b7\u03c2"
|
||||
},
|
||||
"title": "\u0394\u03b9\u03b1\u03bc\u03cc\u03c1\u03c6\u03c9\u03c3\u03b7 AlarmDecoder"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
74
homeassistant/components/alarmdecoder/translations/en.json
Normal file
74
homeassistant/components/alarmdecoder/translations/en.json
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Device is already configured"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Successfully connected to AlarmDecoder."
|
||||
},
|
||||
"error": {
|
||||
"service_unavailable": "Failed to connect"
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"device_baudrate": "Device Baud Rate",
|
||||
"device_path": "Device Path",
|
||||
"host": "Host",
|
||||
"port": "Port"
|
||||
},
|
||||
"title": "Configure connection settings"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "Protocol"
|
||||
},
|
||||
"title": "Choose AlarmDecoder Protocol"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"int": "The field below must be an integer.",
|
||||
"loop_range": "RF Loop must be an integer between 1 and 4.",
|
||||
"loop_rfid": "RF Loop cannot be used without RF Serial.",
|
||||
"relay_inclusive": "Relay Address and Relay Channel are codependent and must be included together."
|
||||
},
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"data": {
|
||||
"alt_night_mode": "Alternative Night Mode",
|
||||
"auto_bypass": "Auto Bypass on Arm",
|
||||
"code_arm_required": "Code Required for Arming"
|
||||
},
|
||||
"title": "Configure AlarmDecoder"
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "Edit"
|
||||
},
|
||||
"description": "What would you like to edit?",
|
||||
"title": "Configure AlarmDecoder"
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_loop": "RF Loop",
|
||||
"zone_name": "Zone Name",
|
||||
"zone_relayaddr": "Relay Address",
|
||||
"zone_relaychan": "Relay Channel",
|
||||
"zone_rfid": "RF Serial",
|
||||
"zone_type": "Zone Type"
|
||||
},
|
||||
"description": "Enter details for zone {zone_number}. To delete zone {zone_number}, leave Zone Name blank.",
|
||||
"title": "Configure AlarmDecoder"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "Zone Number"
|
||||
},
|
||||
"description": "Enter the zone number you'd like to to add, edit, or remove.",
|
||||
"title": "Configure AlarmDecoder"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
74
homeassistant/components/alarmdecoder/translations/es.json
Normal file
74
homeassistant/components/alarmdecoder/translations/es.json
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "El dispositivo AlarmDecoder ya est\u00e1 configurado."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Conectado con \u00e9xito a AlarmDecoder."
|
||||
},
|
||||
"error": {
|
||||
"service_unavailable": "No se pudo conectar"
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"device_baudrate": "Velocidad en baudios del dispositivo",
|
||||
"device_path": "Ruta del dispositivo",
|
||||
"host": "Host",
|
||||
"port": "Puerto"
|
||||
},
|
||||
"title": "Configurar los ajustes de conexi\u00f3n"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "Protocolo"
|
||||
},
|
||||
"title": "Elige el protocolo del AlarmDecoder"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"int": "El campo siguiente debe ser un n\u00famero entero.",
|
||||
"loop_range": "El bucle RF debe ser un n\u00famero entero entre 1 y 4.",
|
||||
"loop_rfid": "El bucle de RF no puede utilizarse sin el serie RF.",
|
||||
"relay_inclusive": "La direcci\u00f3n de retransmisi\u00f3n y el canal de retransmisi\u00f3n son codependientes y deben incluirse a la vez."
|
||||
},
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"data": {
|
||||
"alt_night_mode": "Modo noche alternativo",
|
||||
"auto_bypass": "Desv\u00edo autom\u00e1tico al armar",
|
||||
"code_arm_required": "C\u00f3digo requerido para el armado"
|
||||
},
|
||||
"title": "Configurar AlarmDecoder"
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "Editar"
|
||||
},
|
||||
"description": "\u00bfQu\u00e9 te gustar\u00eda editar?",
|
||||
"title": "Configurar AlarmDecoder"
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_loop": "Bucle RF",
|
||||
"zone_name": "Nombre de zona",
|
||||
"zone_relayaddr": "Direcci\u00f3n de retransmisi\u00f3n",
|
||||
"zone_relaychan": "Canal de retransmisi\u00f3n",
|
||||
"zone_rfid": "Serie RF",
|
||||
"zone_type": "Tipo de zona"
|
||||
},
|
||||
"description": "Introduce los detalles para la zona {zona_number}. Para borrar la zona {zone_number}, deja el nombre de la zona en blanco.",
|
||||
"title": "Configurar AlarmDecoder"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "N\u00famero de zona"
|
||||
},
|
||||
"description": "Introduce el n\u00famero de zona que deseas a\u00f1adir, editar o eliminar.",
|
||||
"title": "Configurar AlarmDecoder"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
49
homeassistant/components/alarmdecoder/translations/et.json
Normal file
49
homeassistant/components/alarmdecoder/translations/et.json
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "Protokoll"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"data": {
|
||||
"alt_night_mode": "Alternatiivne \u00f6\u00f6re\u017eiim",
|
||||
"auto_bypass": "Automaatne m\u00f6\u00f6daviik valvestamisel",
|
||||
"code_arm_required": "Valvestamise kood"
|
||||
},
|
||||
"title": "Seadista AlarmDecoder"
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "Muuda"
|
||||
},
|
||||
"description": "Mida Te soovite muuta?",
|
||||
"title": "Seadista AlarmDecoder"
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_loop": "RF silmus",
|
||||
"zone_name": "Ala nimi",
|
||||
"zone_relayaddr": "Relee aadress",
|
||||
"zone_relaychan": "Relee kanalinumber",
|
||||
"zone_rfid": "RF jada\u00fchendus",
|
||||
"zone_type": "Ala t\u00fc\u00fcp"
|
||||
},
|
||||
"description": "Sisestage ala {zone_number} \u00fcksikasjad. Ala {zone_number} kustutamiseks j\u00e4tke ala nimi t\u00fchjaks.",
|
||||
"title": "Seadista AlarmDecoder"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "Ala number"
|
||||
},
|
||||
"description": "Sisestage ala number mida soovite lisada, muuta v\u00f5i eemaldada.",
|
||||
"title": "Seadista AlarmDecoder"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
74
homeassistant/components/alarmdecoder/translations/fr.json
Normal file
74
homeassistant/components/alarmdecoder/translations/fr.json
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Connexion r\u00e9ussie \u00e0 AlarmDecoder."
|
||||
},
|
||||
"error": {
|
||||
"service_unavailable": "\u00c9chec de connexion"
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"device_baudrate": "D\u00e9bit en bauds de l'appareil",
|
||||
"device_path": "Chemin du p\u00e9riph\u00e9rique",
|
||||
"host": "H\u00f4te",
|
||||
"port": "Port"
|
||||
},
|
||||
"title": "Configurer les param\u00e8tres de connexion"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "Protocole"
|
||||
},
|
||||
"title": "Choisissez le protocole AlarmDecoder"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"int": "Le champ ci-dessous doit \u00eatre un entier.",
|
||||
"loop_range": "La boucle RF doit \u00eatre un entier compris entre 1 et 4.",
|
||||
"loop_rfid": "La boucle RF ne peut pas \u00eatre utilis\u00e9e sans s\u00e9rie RF.",
|
||||
"relay_inclusive": "L'adresse de relais et le canal de relais d\u00e9pendent du codage et doivent \u00eatre inclus ensemble."
|
||||
},
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"data": {
|
||||
"alt_night_mode": "Mode nuit alternatif",
|
||||
"auto_bypass": "Bypass automatique \u00e0 l'armement",
|
||||
"code_arm_required": "Code requis pour l'armement"
|
||||
},
|
||||
"title": "Configurer AlarmDecoder"
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "Modifier"
|
||||
},
|
||||
"description": "Que voulez-vous modifier?",
|
||||
"title": "Configurer AlarmDecoder"
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_loop": "Boucle RF",
|
||||
"zone_name": "Nom de zone",
|
||||
"zone_relayaddr": "Adresse de relais",
|
||||
"zone_relaychan": "Canal de relais",
|
||||
"zone_rfid": "RF S\u00e9rie",
|
||||
"zone_type": "Type de zone"
|
||||
},
|
||||
"description": "Entrez les d\u00e9tails de la zone {zone_number} . Pour supprimer la zone {zone_number} , laissez le nom de zone vide.",
|
||||
"title": "Configurer AlarmDecoder"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "Num\u00e9ro de zone"
|
||||
},
|
||||
"description": "Saisissez le num\u00e9ro de zone que vous souhaitez ajouter, modifier ou supprimer.",
|
||||
"title": "Configurer AlarmDecoder"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
74
homeassistant/components/alarmdecoder/translations/it.json
Normal file
74
homeassistant/components/alarmdecoder/translations/it.json
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Collegato con successo ad AlarmDecoder."
|
||||
},
|
||||
"error": {
|
||||
"service_unavailable": "Impossibile connettersi"
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"device_baudrate": "Velocit\u00e0 di trasmissione del dispositivo",
|
||||
"device_path": "Percorso del dispositivo",
|
||||
"host": "Host",
|
||||
"port": "Porta"
|
||||
},
|
||||
"title": "Configurare le impostazioni di connessione"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "Protocollo"
|
||||
},
|
||||
"title": "Scegliere il protocollo AlarmDecoder"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"int": "Il campo sottostante deve essere un numero intero.",
|
||||
"loop_range": "Il Ciclo RF deve essere un numero intero compreso tra 1 e 4.",
|
||||
"loop_rfid": "Il Ciclo RF non pu\u00f2 essere utilizzato senza il Seriale RF ",
|
||||
"relay_inclusive": "L'indirizzo del rel\u00e8 e il canale del rel\u00e8 sono codipendenti e devono essere inclusi insieme."
|
||||
},
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"data": {
|
||||
"alt_night_mode": "Modalit\u00e0 notturna alternativa",
|
||||
"auto_bypass": "Bypass automatico all'attivazione",
|
||||
"code_arm_required": "Codice richiesto per l'attivazione"
|
||||
},
|
||||
"title": "Configurare AlarmDecoder"
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "Modifica"
|
||||
},
|
||||
"description": "Cosa vorresti modificare?",
|
||||
"title": "Configurare AlarmDecoder"
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_loop": "Ciclo RF",
|
||||
"zone_name": "Nome zona",
|
||||
"zone_relayaddr": "Indirizzo rel\u00e8",
|
||||
"zone_relaychan": "Canale rel\u00e8",
|
||||
"zone_rfid": "Seriale RF",
|
||||
"zone_type": "Tipo di zona"
|
||||
},
|
||||
"description": "Immettere i dettagli per la zona {zone_number}. Per eliminare la zona {zone_number}, lasciare vuoto il campo Nome zona.",
|
||||
"title": "Configurare AlarmDecoder"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "Numero di zona"
|
||||
},
|
||||
"description": "Immettere il numero di zona che si desidera aggiungere, modificare o rimuovere.",
|
||||
"title": "Configurare AlarmDecoder"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
74
homeassistant/components/alarmdecoder/translations/ko.json
Normal file
74
homeassistant/components/alarmdecoder/translations/ko.json
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\uc7a5\uce58\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "AlarmDecoder\uc5d0 \uc131\uacf5\uc801\uc73c\ub85c \uc5f0\uacb0\ub418\uc5c8\uc2b5\ub2c8\ub2e4."
|
||||
},
|
||||
"error": {
|
||||
"service_unavailable": "\uc5f0\uacb0 \uc2e4\ud328"
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"device_baudrate": "\uc7a5\uce58 \uc804\uc1a1 \uc18d\ub3c4",
|
||||
"device_path": "\uc7a5\uce58 \uacbd\ub85c",
|
||||
"host": "\ud638\uc2a4\ud2b8",
|
||||
"port": "\ud3ec\ud2b8"
|
||||
},
|
||||
"title": "\uc5f0\uacb0 \uc124\uc815 \uad6c\uc131"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "\ud504\ub85c\ud1a0\ucf5c"
|
||||
},
|
||||
"title": "AlarmDecoder \ud504\ub85c\ud1a0\ucf5c \uc120\ud0dd"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"int": "\uc544\ub798 \ud544\ub4dc\ub294 \uc815\uc218\uc5ec\uc57c \ud569\ub2c8\ub2e4.",
|
||||
"loop_range": "RF \ub8e8\ud504\ub294 1\uc5d0\uc11c 4 \uc0ac\uc774\uc758 \uc815\uc218\uc5ec\uc57c \ud569\ub2c8\ub2e4.",
|
||||
"loop_rfid": "RF \ub8e8\ud504\ub294 RF \uc2dc\ub9ac\uc5bc\uc5c6\uc774 \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.",
|
||||
"relay_inclusive": "\ub9b4\ub808\uc774 \uc8fc\uc18c\uc640 \ub9b4\ub808\uc774 \ucc44\ub110\uc740 \uc11c\ub85c \uc758\uc874\uc801\uc774\uba70 \ud568\uaed8 \ud3ec\ud568\ub418\uc5b4\uc57c\ud569\ub2c8\ub2e4."
|
||||
},
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"data": {
|
||||
"alt_night_mode": "\ub300\uccb4 \uc57c\uac04 \ubaa8\ub4dc",
|
||||
"auto_bypass": "\uacbd\ube44\uc911 \uc790\ub3d9 \uc6b0\ud68c",
|
||||
"code_arm_required": "\uacbd\ube44\uc5d0 \ud544\uc694\ud55c \ucf54\ub4dc"
|
||||
},
|
||||
"title": "AlarmDecoder \uad6c\uc131"
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "\ud3b8\uc9d1"
|
||||
},
|
||||
"description": "\ubb34\uc5c7\uc744 \ud3b8\uc9d1 \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?",
|
||||
"title": "AlarmDecoder \uad6c\uc131"
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_loop": "RF \ub8e8\ud504",
|
||||
"zone_name": "\uc601\uc5ed \uc774\ub984",
|
||||
"zone_relayaddr": "\ub9b4\ub808\uc774 \uc8fc\uc18c",
|
||||
"zone_relaychan": "\ub9b4\ub808\uc774 \ucc44\ub110",
|
||||
"zone_rfid": "RF \uc2dc\ub9ac\uc5bc",
|
||||
"zone_type": "\uc601\uc5ed \uc720\ud615"
|
||||
},
|
||||
"description": "{zone_number} \uc601\uc5ed\uc5d0 \ub300\ud55c \uc138\ubd80 \uc815\ubcf4\ub97c \uc785\ub825\ud569\ub2c8\ub2e4. {zone_number} \uc601\uc5ed\uc744 \uc0ad\uc81c\ud558\ub824\uba74 \uc601\uc5ed \uc774\ub984\uc744 \ube44\uc6cc \ub461\ub2c8\ub2e4.",
|
||||
"title": "AlarmDecoder \uad6c\uc131"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "\uad6c\uc5ed \ubc88\ud638"
|
||||
},
|
||||
"description": "\ucd94\uac00, \ud3b8\uc9d1 \ub610\ub294 \uc81c\uac70\ud560 \uc601\uc5ed \ubc88\ud638\ub97c \uc785\ub825\ud569\ub2c8\ub2e4.",
|
||||
"title": "AlarmDecoder \uad6c\uc131"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
64
homeassistant/components/alarmdecoder/translations/lb.json
Normal file
64
homeassistant/components/alarmdecoder/translations/lb.json
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Apparat ass scho konfigur\u00e9iert"
|
||||
},
|
||||
"error": {
|
||||
"service_unavailable": "Feeler beim verbannen"
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"device_baudrate": "Apparat Baudrate",
|
||||
"device_path": "Pad vum Apparat",
|
||||
"host": "Host",
|
||||
"port": "Port"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "Protokoll"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"int": "D'Feld hei \u00ebnnen muss eng ganz Zuel sinn.",
|
||||
"relay_inclusive": "Relais Adress a Relais Kanal sin vuneneen ofh\u00e4ngeg a musse mat abegraff sinn."
|
||||
},
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"data": {
|
||||
"alt_night_mode": "Alternative Nuecht Modus",
|
||||
"auto_bypass": "Auto Bypass beim aktiv\u00e9ieren",
|
||||
"code_arm_required": "Code erfuerderlech fir d'Aktiv\u00e9ierung"
|
||||
},
|
||||
"title": "AlarmDecoder konfigur\u00e9ieren"
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "\u00c4nneren"
|
||||
},
|
||||
"description": "Wat w\u00eblls du \u00e4nneren?",
|
||||
"title": "AlarmDecoder konfigur\u00e9ieren"
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_loop": "RF Schleef",
|
||||
"zone_name": "Numm vun der Zone",
|
||||
"zone_relayaddr": "Relais Adresse",
|
||||
"zone_relaychan": "Relais Kanal",
|
||||
"zone_rfid": "RF Serielle",
|
||||
"zone_type": "Type vun der Zone"
|
||||
},
|
||||
"title": "AlarmDecoder konfigur\u00e9ieren"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "Zone Nummer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
73
homeassistant/components/alarmdecoder/translations/nl.json
Normal file
73
homeassistant/components/alarmdecoder/translations/nl.json
Normal file
@ -0,0 +1,73 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "AlarmDecoder-apparaat is al geconfigureerd."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Succesvol verbonden met AlarmDecoder."
|
||||
},
|
||||
"error": {
|
||||
"service_unavailable": "Kon niet verbinden"
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"device_baudrate": "Baudrate van apparaat",
|
||||
"device_path": "Apparaatpad",
|
||||
"host": "Host",
|
||||
"port": "Poort"
|
||||
},
|
||||
"title": "Configureer de verbindingsinstellingen"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "Protocol"
|
||||
},
|
||||
"title": "Kies AlarmDecoder Protocol"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"int": "Het onderstaande veld moet een geheel getal zijn.",
|
||||
"loop_range": "RF Lus moet een geheel getal zijn tussen 1 en 4.",
|
||||
"loop_rfid": "RF Lus kan niet worden gebruikt zonder RF Serieel.",
|
||||
"relay_inclusive": "Het relais-adres en het relais-kanaal zijn codeafhankelijk en moeten samen worden opgenomen."
|
||||
},
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"data": {
|
||||
"alt_night_mode": "Alternatieve nachtmodus",
|
||||
"auto_bypass": "Automatische bypass bij inschakelen",
|
||||
"code_arm_required": "Code vereist voor inschakelen"
|
||||
},
|
||||
"title": "Configureer AlarmDecoder"
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "Bewerk"
|
||||
},
|
||||
"description": "Wat wilt u bewerken?",
|
||||
"title": "Configureer AlarmDecoder"
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_loop": "RF Lus",
|
||||
"zone_name": "Zone naam",
|
||||
"zone_relayaddr": "Relais Adres",
|
||||
"zone_relaychan": "Relais Kanaal",
|
||||
"zone_rfid": "RF Serieel",
|
||||
"zone_type": "Zone Type"
|
||||
},
|
||||
"title": "Configureer AlarmDecoder"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "Zone nummer"
|
||||
},
|
||||
"description": "Voer het zone nummer in dat u wilt toevoegen, bewerken of verwijderen.",
|
||||
"title": "Configureer AlarmDecoder"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
74
homeassistant/components/alarmdecoder/translations/no.json
Normal file
74
homeassistant/components/alarmdecoder/translations/no.json
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Enheten er allerede konfigurert"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Vellykket koblet til AlarmDecoder."
|
||||
},
|
||||
"error": {
|
||||
"service_unavailable": "Tilkobling mislyktes."
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"device_baudrate": "Baud-hastighet for enhet",
|
||||
"device_path": "Bane til enheten",
|
||||
"host": "Vert",
|
||||
"port": "Port"
|
||||
},
|
||||
"title": "Konfigurer tilkoblingsinnstillinger"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "Protokoll"
|
||||
},
|
||||
"title": "Velg AlarmDecoder Protokoll"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"int": "Feltet nedenfor m\u00e5 v\u00e6re et helt tall.",
|
||||
"loop_range": "RF Loop m\u00e5 v\u00e6re et heltall mellom 1 og 4.",
|
||||
"loop_rfid": "RF Loop kan ikke brukes uten RF Serial.",
|
||||
"relay_inclusive": "Rel\u00e9adresse og rel\u00e9kanal er kodeavhengige og m\u00e5 inkluderes sammen."
|
||||
},
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"data": {
|
||||
"alt_night_mode": "Alternativ nattmodus",
|
||||
"auto_bypass": "Auto bypass p\u00e5 Arm",
|
||||
"code_arm_required": "Kode kreves for tilkobling"
|
||||
},
|
||||
"title": "Konfigurer AlarmDecoder"
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "Rediger"
|
||||
},
|
||||
"description": "Hva \u00f8nsker du \u00e5 redigere?",
|
||||
"title": "Konfigurer AlarmDecoder"
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_loop": "RF Loop",
|
||||
"zone_name": "Sonenavn",
|
||||
"zone_relayaddr": "Rel\u00e9 adresse",
|
||||
"zone_relaychan": "Rel\u00e9 kanal",
|
||||
"zone_rfid": "RF seriell",
|
||||
"zone_type": "Sone type"
|
||||
},
|
||||
"description": "Angi detaljer for sonen {zone_number}. Hvis du vil slette sonen {zone_number}, lar du Sonenavn st\u00e5 tomt.",
|
||||
"title": "Konfigurer AlarmDecoder"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "Sone nummer"
|
||||
},
|
||||
"description": "Angi sonenummeret du vil legge til, redigere eller fjerne.",
|
||||
"title": "Konfigurer AlarmDecoder"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
homeassistant/components/alarmdecoder/translations/pl.json
Normal file
37
homeassistant/components/alarmdecoder/translations/pl.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane"
|
||||
},
|
||||
"error": {
|
||||
"service_unavailable": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia"
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"host": "Nazwa hosta lub adres IP",
|
||||
"port": "Port"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "Edytuj"
|
||||
}
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_relaychan": "Kana\u0142 przeka\u017anika"
|
||||
}
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "Numer strefy"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
74
homeassistant/components/alarmdecoder/translations/ru.json
Normal file
74
homeassistant/components/alarmdecoder/translations/ru.json
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"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."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043e \u043a AlarmDecoder."
|
||||
},
|
||||
"error": {
|
||||
"service_unavailable": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f."
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"device_baudrate": "\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430",
|
||||
"device_path": "\u041f\u0443\u0442\u044c \u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0443",
|
||||
"host": "\u0425\u043e\u0441\u0442",
|
||||
"port": "\u041f\u043e\u0440\u0442"
|
||||
},
|
||||
"title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b"
|
||||
},
|
||||
"title": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b AlarmDecoder"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"int": "\u041f\u043e\u043b\u0435 \u043d\u0438\u0436\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0446\u0435\u043b\u044b\u043c \u0447\u0438\u0441\u043b\u043e\u043c.",
|
||||
"loop_range": "RF Loop \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0446\u0435\u043b\u044b\u043c \u0447\u0438\u0441\u043b\u043e\u043c \u043e\u0442 1 \u0434\u043e 4.",
|
||||
"loop_rfid": "RF Loop \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0431\u0435\u0437 RF Serial.",
|
||||
"relay_inclusive": "\u0410\u0434\u0440\u0435\u0441 \u0440\u0435\u043b\u0435 \u0438 \u043a\u0430\u043d\u0430\u043b \u0440\u0435\u043b\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b \u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0432\u043c\u0435\u0441\u0442\u0435."
|
||||
},
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"data": {
|
||||
"alt_night_mode": "\u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u043d\u043e\u0447\u043d\u043e\u0439 \u0440\u0435\u0436\u0438\u043c",
|
||||
"auto_bypass": "\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u043f\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435 \u043d\u0430 \u043e\u0445\u0440\u0430\u043d\u0443",
|
||||
"code_arm_required": "\u041a\u043e\u0434, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0439 \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043d\u0430 \u043e\u0445\u0440\u0430\u043d\u0443"
|
||||
},
|
||||
"title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 AlarmDecoder"
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c"
|
||||
},
|
||||
"description": "\u0427\u0442\u043e \u0431\u044b \u0412\u044b \u0445\u043e\u0442\u0435\u043b\u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c?",
|
||||
"title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 AlarmDecoder"
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_loop": "RF Loop",
|
||||
"zone_name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0437\u043e\u043d\u044b",
|
||||
"zone_relayaddr": "\u0410\u0434\u0440\u0435\u0441 \u0440\u0435\u043b\u0435",
|
||||
"zone_relaychan": "\u041a\u0430\u043d\u0430\u043b \u0440\u0435\u043b\u0435",
|
||||
"zone_rfid": "RF Serial",
|
||||
"zone_type": "\u0422\u0438\u043f \u0437\u043e\u043d\u044b"
|
||||
},
|
||||
"description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0437\u043e\u043d\u044b {zone_number}. \u0427\u0442\u043e\u0431\u044b \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0437\u043e\u043d\u0443 {zone_number}, \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u0435 \"\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0437\u043e\u043d\u044b\" \u043f\u0443\u0441\u0442\u044b\u043c.",
|
||||
"title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 AlarmDecoder"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "\u041d\u043e\u043c\u0435\u0440 \u0437\u043e\u043d\u044b"
|
||||
},
|
||||
"description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043d\u043e\u043c\u0435\u0440 \u0437\u043e\u043d\u044b, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c, \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0438\u043b\u0438 \u0443\u0434\u0430\u043b\u0438\u0442\u044c.",
|
||||
"title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 AlarmDecoder"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
homeassistant/components/alarmdecoder/translations/sv.json
Normal file
27
homeassistant/components/alarmdecoder/translations/sv.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"device_path": "Enhetsv\u00e4g"
|
||||
},
|
||||
"title": "Konfigurera anslutningsinst\u00e4llningar"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "Protokoll"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "Redigera"
|
||||
},
|
||||
"description": "Vad vill du redigera?"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u8a2d\u5099\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "\u6210\u529f\u9023\u7dda\u81f3 AlarmDecoder\u3002"
|
||||
},
|
||||
"error": {
|
||||
"service_unavailable": "\u9023\u7dda\u5931\u6557"
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"device_baudrate": "\u8a2d\u5099\u901a\u8a0a\u7387",
|
||||
"device_path": "\u8a2d\u5099\u8def\u5f91",
|
||||
"host": "\u4e3b\u6a5f\u7aef",
|
||||
"port": "\u901a\u8a0a\u57e0"
|
||||
},
|
||||
"title": "\u8a2d\u5b9a\u9023\u7dda\u8a2d\u5b9a"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "\u901a\u8a0a\u5354\u5b9a"
|
||||
},
|
||||
"title": "\u9078\u64c7 AlarmDecoder \u901a\u8a0a\u5354\u5b9a"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"int": "\u4e0b\u65b9\u6b04\u4f4d\u5fc5\u9808\u70ba\u6574\u6578\u3002",
|
||||
"loop_range": "RF \u8ff4\u8def\u5fc5\u9808\u70ba\u4ecb\u65bc 1 \u81f3 4 \u9593\u7684\u6574\u6578\u3002",
|
||||
"loop_rfid": "\u5982\u679c\u6c92\u6709 RF \u5e8f\u5217\u5247\u7121\u6cd5\u4f7f\u7528 RF \u8ff4\u8def\u3002",
|
||||
"relay_inclusive": "\u4e2d\u7e7c\u5730\u5740\u8207\u4e2d\u7e7c\u983b\u9053\u70ba\u76f8\u4e92\u4f9d\u8cf4\uff0c\u4e26\u5fc5\u9808\u4e00\u8d77\u5305\u542b\u3002"
|
||||
},
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"data": {
|
||||
"alt_night_mode": "\u66ff\u4ee3\u591c\u9593\u6a21\u5f0f",
|
||||
"auto_bypass": "\u81ea\u52d5\u5ffd\u7565\u8b66\u6212",
|
||||
"code_arm_required": "\u8b66\u6212\u9700\u8981\u4ee3\u78bc"
|
||||
},
|
||||
"title": "\u8a2d\u5b9a AlarmDecoder"
|
||||
},
|
||||
"init": {
|
||||
"data": {
|
||||
"edit_select": "\u7de8\u8f2f"
|
||||
},
|
||||
"description": "\u662f\u5426\u8981\u9032\u884c\u7de8\u8f2f\uff1f",
|
||||
"title": "\u8a2d\u5b9a AlarmDecoder"
|
||||
},
|
||||
"zone_details": {
|
||||
"data": {
|
||||
"zone_loop": "RF \u8ff4\u8def",
|
||||
"zone_name": "\u5340\u57df\u540d\u7a31",
|
||||
"zone_relayaddr": "\u4e2d\u7e7c\u4f4d\u5740",
|
||||
"zone_relaychan": "\u4e2d\u7e7c\u983b\u9053",
|
||||
"zone_rfid": "RF \u5e8f\u5217",
|
||||
"zone_type": "\u5340\u57df\u985e\u578b"
|
||||
},
|
||||
"description": "\u8f38\u5165\u5340\u57df {zone_number} \u8a73\u7d30\u8cc7\u6599\u3002\u6b32\u522a\u9664\u5340\u57df {zone_number}\uff0c\u4fdd\u6301\u5340\u57df\u540d\u7a31\u7a7a\u767d\u3002",
|
||||
"title": "\u8a2d\u5b9a AlarmDecoder"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "\u5340\u57df\u78bc"
|
||||
},
|
||||
"description": "\u8f38\u5165\u6240\u8981\u65b0\u589e\u3001\u7de8\u8f2f\u6216\u79fb\u9664\u7684\u5340\u57df\u78bc\u3002",
|
||||
"title": "\u8a2d\u5b9a AlarmDecoder"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -33,6 +33,7 @@ from homeassistant.const import (
|
||||
CONF_NAME,
|
||||
TEMP_CELSIUS,
|
||||
TEMP_FAHRENHEIT,
|
||||
__version__,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, State, callback
|
||||
from homeassistant.helpers import network
|
||||
@ -286,6 +287,12 @@ class AlexaEntity:
|
||||
"friendlyName": self.friendly_name(),
|
||||
"description": self.description(),
|
||||
"manufacturerName": "Home Assistant",
|
||||
"additionalAttributes": {
|
||||
"manufacturer": "Home Assistant",
|
||||
"model": self.entity.domain,
|
||||
"softwareVersion": __version__,
|
||||
"customIdentifier": self.entity_id,
|
||||
},
|
||||
}
|
||||
|
||||
locale = self.config.locale
|
||||
|
@ -17,6 +17,7 @@ from homeassistant.components import (
|
||||
from homeassistant.components.climate import const as climate
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_ENTITY_PICTURE,
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
ATTR_TEMPERATURE,
|
||||
SERVICE_ALARM_ARM_AWAY,
|
||||
@ -1532,7 +1533,7 @@ async def async_api_initialize_camera_stream(hass, config, directive, context):
|
||||
"""Process a InitializeCameraStreams request."""
|
||||
entity = directive.entity
|
||||
stream_source = await camera.async_request_stream(hass, entity.entity_id, fmt="hls")
|
||||
camera_image = hass.states.get(entity.entity_id).attributes["entity_picture"]
|
||||
camera_image = hass.states.get(entity.entity_id).attributes[ATTR_ENTITY_PICTURE]
|
||||
|
||||
try:
|
||||
external_url = network.get_url(
|
||||
|
@ -6,7 +6,7 @@ import logging
|
||||
import aiohttp
|
||||
import async_timeout
|
||||
|
||||
from homeassistant.const import MATCH_ALL, STATE_ON
|
||||
from homeassistant.const import HTTP_ACCEPTED, MATCH_ALL, STATE_ON
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from .const import API_CHANGE, Cause
|
||||
@ -109,7 +109,7 @@ async def async_send_changereport_message(
|
||||
_LOGGER.debug("Sent: %s", json.dumps(message_serialized))
|
||||
_LOGGER.debug("Received (%s): %s", response.status, response_text)
|
||||
|
||||
if response.status == 202:
|
||||
if response.status == HTTP_ACCEPTED:
|
||||
return
|
||||
|
||||
response_json = json.loads(response_text)
|
||||
@ -240,7 +240,7 @@ async def async_send_doorbell_event_message(hass, config, alexa_entity):
|
||||
_LOGGER.debug("Sent: %s", json.dumps(message_serialized))
|
||||
_LOGGER.debug("Received (%s): %s", response.status, response_text)
|
||||
|
||||
if response.status == 202:
|
||||
if response.status == HTTP_ACCEPTED:
|
||||
return
|
||||
|
||||
response_json = json.loads(response_text)
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"pick_implementation": { "title": "Pick Authentication Method" },
|
||||
"pick_implementation": { "title": "[%key:common::config_flow::title::oauth2_pick_implementation%]" },
|
||||
"hassio_confirm": {
|
||||
"title": "Almond via Hass.io add-on",
|
||||
"description": "Do you want to configure Home Assistant to connect to Almond provided by the Hass.io add-on: {addon}?"
|
||||
|
@ -3,7 +3,8 @@
|
||||
"abort": {
|
||||
"already_setup": "Nom\u00e9s pots configurar un \u00fanic compte amb Almond.",
|
||||
"cannot_connect": "No es pot connectar amb el servidor d'Almond.",
|
||||
"missing_configuration": "Consulta la documentaci\u00f3 sobre com configurar Almond."
|
||||
"missing_configuration": "Consulta la documentaci\u00f3 sobre com configurar Almond.",
|
||||
"no_url_available": "No hi ha cap URL disponible. Per a m\u00e9s informaci\u00f3 sobre aquest error, [consulta la secci\u00f3 d'ajuda]({docs_url})"
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
|
@ -3,7 +3,8 @@
|
||||
"abort": {
|
||||
"already_setup": "You can only configure one Almond account.",
|
||||
"cannot_connect": "Unable to connect to the Almond server.",
|
||||
"missing_configuration": "Please check the documentation on how to set up Almond."
|
||||
"missing_configuration": "Please check the documentation on how to set up Almond.",
|
||||
"no_url_available": "No URL available. For information about this error, [check the help section]({docs_url})"
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
|
@ -3,7 +3,8 @@
|
||||
"abort": {
|
||||
"already_setup": "S\u00f3lo puede configurar una cuenta de Almond.",
|
||||
"cannot_connect": "No se puede conectar al servidor Almond.",
|
||||
"missing_configuration": "Consulte la documentaci\u00f3n sobre c\u00f3mo configurar Almond."
|
||||
"missing_configuration": "Consulte la documentaci\u00f3n sobre c\u00f3mo configurar Almond.",
|
||||
"no_url_available": "No hay URL disponible. Para obtener informaci\u00f3n sobre este error, [consulta la secci\u00f3n de ayuda]({docs_url})"
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
|
@ -3,7 +3,8 @@
|
||||
"abort": {
|
||||
"already_setup": "Vous ne pouvez configurer qu'un seul compte Almond",
|
||||
"cannot_connect": "Impossible de se connecter au serveur Almond",
|
||||
"missing_configuration": "Veuillez consulter la documentation pour savoir comment configurer Almond."
|
||||
"missing_configuration": "Veuillez consulter la documentation pour savoir comment configurer Almond.",
|
||||
"no_url_available": "Aucune URL disponible. Pour plus d'informations sur cette erreur, [consultez la section d'aide] ( {docs_url} )"
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
|
@ -3,7 +3,8 @@
|
||||
"abort": {
|
||||
"already_setup": "\u00c8 possibile configurare un solo account Almond.",
|
||||
"cannot_connect": "Impossibile connettersi al server Almond.",
|
||||
"missing_configuration": "Si prega di controllare la documentazione su come impostare Almond."
|
||||
"missing_configuration": "Si prega di controllare la documentazione su come impostare Almond.",
|
||||
"no_url_available": "Nessun URL disponibile. Per informazioni su questo errore, [controlla la sezione della guida]({docs_url})"
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
@ -11,7 +12,7 @@
|
||||
"title": "Almond tramite il componente aggiuntivo di Hass.io"
|
||||
},
|
||||
"pick_implementation": {
|
||||
"title": "Seleziona metodo di autenticazione"
|
||||
"title": "Scegli il metodo di autenticazione"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,8 @@
|
||||
"abort": {
|
||||
"already_setup": "\ud558\ub098\uc758 Almond \uacc4\uc815\ub9cc \uad6c\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.",
|
||||
"cannot_connect": "Almond \uc11c\ubc84\uc5d0 \uc5f0\uacb0\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.",
|
||||
"missing_configuration": "Almond \uc124\uc815 \ubc29\ubc95\uc5d0 \ub300\ud55c \uc124\uba85\uc11c\ub97c \ud655\uc778\ud574\uc8fc\uc138\uc694."
|
||||
"missing_configuration": "Almond \uc124\uc815 \ubc29\ubc95\uc5d0 \ub300\ud55c \uc124\uba85\uc11c\ub97c \ud655\uc778\ud574\uc8fc\uc138\uc694.",
|
||||
"no_url_available": "\uac00\ub2a5\ud55c URL\uc774 \uc5c6\uc2b5\ub2c8\ub2e4. \uc774 \uc5d0\ub7ec\uc5d0 \ub300\ud55c \uc815\ubcf4\ub294 \ub3c4\uc6c0\ub9d0 \uc139\uc158\uc744 \ud655\uc778\ud558\uc138\uc694({docs_url})"
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
|
@ -3,7 +3,8 @@
|
||||
"abort": {
|
||||
"already_setup": "Dir k\u00ebnnt n\u00ebmmen een eenzegen Almond Kont konfigur\u00e9ieren.",
|
||||
"cannot_connect": "Kann sech net mam Almond Server verbannen.",
|
||||
"missing_configuration": "Kuckt w.e.g. Dokumentatioun iwwert d'ariichten vun Almond."
|
||||
"missing_configuration": "Kuckt w.e.g. Dokumentatioun iwwert d'ariichten vun Almond.",
|
||||
"no_url_available": "Keng URL disponibel. Fir Informatiounen iwwert d\u00ebse Feeler, [kuck H\u00ebllef Sektioun]({docs_url})"
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
|
@ -3,7 +3,8 @@
|
||||
"abort": {
|
||||
"already_setup": "U kunt slechts \u00e9\u00e9n Almond-account configureren.",
|
||||
"cannot_connect": "Kan geen verbinding maken met de Almond-server.",
|
||||
"missing_configuration": "Raadpleeg de documentatie over het instellen van Almond."
|
||||
"missing_configuration": "Raadpleeg de documentatie over het instellen van Almond.",
|
||||
"no_url_available": "Geen URL beschikbaar. Voor informatie over deze fout, [check de helpsectie]({docs_url})"
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
|
@ -3,11 +3,13 @@
|
||||
"abort": {
|
||||
"already_setup": "Du kan bare konfigurere en Almond konto.",
|
||||
"cannot_connect": "Kan ikke koble til Almond-serveren.",
|
||||
"missing_configuration": "Vennligst sjekk dokumentasjonen om hvordan du setter opp Almond."
|
||||
"missing_configuration": "Vennligst sjekk dokumentasjonen om hvordan du setter opp Almond.",
|
||||
"no_url_available": "Ingen URL tilgjengelig. For informasjon om denne feilen, [sjekk {docs_url} ] ( {docs_url} )"
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
"description": "Vil du konfigurere Home Assistant til \u00e5 koble til Almond levert av Hass.io add-on: {addon}?"
|
||||
"description": "Vil du konfigurere Home Assistant til \u00e5 koble til Almond levert av Hass.io add-on: {addon}?",
|
||||
"title": ""
|
||||
},
|
||||
"pick_implementation": {
|
||||
"title": "Velg godkjenningsmetode"
|
||||
|
@ -3,7 +3,8 @@
|
||||
"abort": {
|
||||
"already_setup": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.",
|
||||
"cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 Almond.",
|
||||
"missing_configuration": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438 \u043f\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 Almond."
|
||||
"missing_configuration": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\u043c\u0438 \u043f\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 Almond.",
|
||||
"no_url_available": "URL-\u0430\u0434\u0440\u0435\u0441 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 [\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439]({docs_url}) \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u0431 \u044d\u0442\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0435."
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
|
@ -3,7 +3,8 @@
|
||||
"abort": {
|
||||
"already_setup": "\u50c5\u80fd\u8a2d\u5b9a\u4e00\u7d44 Almond \u5e33\u865f\u3002",
|
||||
"cannot_connect": "\u7121\u6cd5\u9023\u7dda\u81f3 Almond \u4f3a\u670d\u5668\u3002",
|
||||
"missing_configuration": "\u8acb\u53c3\u8003\u76f8\u95dc\u6587\u4ef6\u4ee5\u4e86\u89e3\u5982\u4f55\u8a2d\u5b9a Almond\u3002"
|
||||
"missing_configuration": "\u8acb\u53c3\u8003\u76f8\u95dc\u6587\u4ef6\u4ee5\u4e86\u89e3\u5982\u4f55\u8a2d\u5b9a Almond\u3002",
|
||||
"no_url_available": "\u6c92\u6709\u53ef\u7528\u7684\u7db2\u5740\u3002\u95dc\u65bc\u6b64\u932f\u8aa4\u66f4\u8a73\u7d30\u8a0a\u606f\uff0c[\u9ede\u9078\u5354\u52a9\u7ae0\u7bc0]({docs_url})"
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
|
@ -6,8 +6,10 @@ from aioambient import Client
|
||||
from aioambient.errors import WebsocketError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.binary_sensor import DEVICE_CLASS_CONNECTIVITY
|
||||
from homeassistant.config_entries import SOURCE_IMPORT
|
||||
from homeassistant.const import (
|
||||
AREA_SQUARE_METERS,
|
||||
ATTR_LOCATION,
|
||||
ATTR_NAME,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
@ -15,8 +17,10 @@ from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
DEGREE,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
LIGHT_LUX,
|
||||
PERCENTAGE,
|
||||
POWER_WATT,
|
||||
PRESSURE_INHG,
|
||||
SPEED_MILES_PER_HOUR,
|
||||
TEMP_FAHRENHEIT,
|
||||
)
|
||||
@ -141,8 +145,8 @@ TYPE_WINDSPEEDMPH = "windspeedmph"
|
||||
TYPE_YEARLYRAININ = "yearlyrainin"
|
||||
SENSOR_TYPES = {
|
||||
TYPE_24HOURRAININ: ("24 Hr Rain", "in", TYPE_SENSOR, None),
|
||||
TYPE_BAROMABSIN: ("Abs Pressure", "inHg", TYPE_SENSOR, "pressure"),
|
||||
TYPE_BAROMRELIN: ("Rel Pressure", "inHg", TYPE_SENSOR, "pressure"),
|
||||
TYPE_BAROMABSIN: ("Abs Pressure", PRESSURE_INHG, TYPE_SENSOR, "pressure"),
|
||||
TYPE_BAROMRELIN: ("Rel Pressure", PRESSURE_INHG, TYPE_SENSOR, "pressure"),
|
||||
TYPE_BATT10: ("Battery 10", None, TYPE_BINARY_SENSOR, "battery"),
|
||||
TYPE_BATT1: ("Battery 1", None, TYPE_BINARY_SENSOR, "battery"),
|
||||
TYPE_BATT2: ("Battery 2", None, TYPE_BINARY_SENSOR, "battery"),
|
||||
@ -175,16 +179,16 @@ SENSOR_TYPES = {
|
||||
TYPE_LASTRAIN: ("Last Rain", None, TYPE_SENSOR, "timestamp"),
|
||||
TYPE_MAXDAILYGUST: ("Max Gust", SPEED_MILES_PER_HOUR, TYPE_SENSOR, None),
|
||||
TYPE_MONTHLYRAININ: ("Monthly Rain", "in", TYPE_SENSOR, None),
|
||||
TYPE_RELAY10: ("Relay 10", None, TYPE_BINARY_SENSOR, "connectivity"),
|
||||
TYPE_RELAY1: ("Relay 1", None, TYPE_BINARY_SENSOR, "connectivity"),
|
||||
TYPE_RELAY2: ("Relay 2", None, TYPE_BINARY_SENSOR, "connectivity"),
|
||||
TYPE_RELAY3: ("Relay 3", None, TYPE_BINARY_SENSOR, "connectivity"),
|
||||
TYPE_RELAY4: ("Relay 4", None, TYPE_BINARY_SENSOR, "connectivity"),
|
||||
TYPE_RELAY5: ("Relay 5", None, TYPE_BINARY_SENSOR, "connectivity"),
|
||||
TYPE_RELAY6: ("Relay 6", None, TYPE_BINARY_SENSOR, "connectivity"),
|
||||
TYPE_RELAY7: ("Relay 7", None, TYPE_BINARY_SENSOR, "connectivity"),
|
||||
TYPE_RELAY8: ("Relay 8", None, TYPE_BINARY_SENSOR, "connectivity"),
|
||||
TYPE_RELAY9: ("Relay 9", None, TYPE_BINARY_SENSOR, "connectivity"),
|
||||
TYPE_RELAY10: ("Relay 10", None, TYPE_BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY1: ("Relay 1", None, TYPE_BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY2: ("Relay 2", None, TYPE_BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY3: ("Relay 3", None, TYPE_BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY4: ("Relay 4", None, TYPE_BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY5: ("Relay 5", None, TYPE_BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY6: ("Relay 6", None, TYPE_BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY7: ("Relay 7", None, TYPE_BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY8: ("Relay 8", None, TYPE_BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_RELAY9: ("Relay 9", None, TYPE_BINARY_SENSOR, DEVICE_CLASS_CONNECTIVITY),
|
||||
TYPE_SOILHUM10: ("Soil Humidity 10", PERCENTAGE, TYPE_SENSOR, "humidity"),
|
||||
TYPE_SOILHUM1: ("Soil Humidity 1", PERCENTAGE, TYPE_SENSOR, "humidity"),
|
||||
TYPE_SOILHUM2: ("Soil Humidity 2", PERCENTAGE, TYPE_SENSOR, "humidity"),
|
||||
@ -205,8 +209,13 @@ SENSOR_TYPES = {
|
||||
TYPE_SOILTEMP7F: ("Soil Temp 7", TEMP_FAHRENHEIT, TYPE_SENSOR, "temperature"),
|
||||
TYPE_SOILTEMP8F: ("Soil Temp 8", TEMP_FAHRENHEIT, TYPE_SENSOR, "temperature"),
|
||||
TYPE_SOILTEMP9F: ("Soil Temp 9", TEMP_FAHRENHEIT, TYPE_SENSOR, "temperature"),
|
||||
TYPE_SOLARRADIATION: ("Solar Rad", f"{POWER_WATT}/m^2", TYPE_SENSOR, None),
|
||||
TYPE_SOLARRADIATION_LX: ("Solar Rad (lx)", "lx", TYPE_SENSOR, "illuminance"),
|
||||
TYPE_SOLARRADIATION: (
|
||||
"Solar Rad",
|
||||
f"{POWER_WATT}/{AREA_SQUARE_METERS}",
|
||||
TYPE_SENSOR,
|
||||
None,
|
||||
),
|
||||
TYPE_SOLARRADIATION_LX: ("Solar Rad (lx)", LIGHT_LUX, TYPE_SENSOR, "illuminance"),
|
||||
TYPE_TEMP10F: ("Temp 10", TEMP_FAHRENHEIT, TYPE_SENSOR, "temperature"),
|
||||
TYPE_TEMP1F: ("Temp 1", TEMP_FAHRENHEIT, TYPE_SENSOR, "temperature"),
|
||||
TYPE_TEMP2F: ("Temp 2", TEMP_FAHRENHEIT, TYPE_SENSOR, "temperature"),
|
||||
|
@ -1,5 +1,8 @@
|
||||
"""Support for Android IP Webcam binary sensors."""
|
||||
from homeassistant.components.binary_sensor import BinarySensorEntity
|
||||
from homeassistant.components.binary_sensor import (
|
||||
DEVICE_CLASS_MOTION,
|
||||
BinarySensorEntity,
|
||||
)
|
||||
|
||||
from . import CONF_HOST, CONF_NAME, DATA_IP_WEBCAM, KEY_MAP, AndroidIPCamEntity
|
||||
|
||||
@ -47,4 +50,4 @@ class IPWebcamBinarySensor(AndroidIPCamEntity, BinarySensorEntity):
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the class of this device, from component DEVICE_CLASSES."""
|
||||
return "motion"
|
||||
return DEVICE_CLASS_MOTION
|
||||
|
@ -502,14 +502,23 @@ class ADBDevice(MediaPlayerEntity):
|
||||
return self._unique_id
|
||||
|
||||
@adb_decorator()
|
||||
async def _adb_screencap(self):
|
||||
"""Take a screen capture from the device."""
|
||||
return await self.aftv.adb_screencap()
|
||||
|
||||
async def async_get_media_image(self):
|
||||
"""Fetch current playing image."""
|
||||
if not self._screencap or self.state in [STATE_OFF, None] or not self.available:
|
||||
return None, None
|
||||
|
||||
media_data = await self.aftv.adb_screencap()
|
||||
media_data = await self._adb_screencap()
|
||||
if media_data:
|
||||
return media_data, "image/png"
|
||||
|
||||
# If an exception occurred and the device is no longer available, write the state
|
||||
if not self.available:
|
||||
self.async_write_ha_state()
|
||||
|
||||
return None, None
|
||||
|
||||
@adb_decorator()
|
||||
|
@ -38,6 +38,7 @@ from homeassistant.helpers.json import JSONEncoder
|
||||
from homeassistant.helpers.network import NoURLAvailableError, get_url
|
||||
from homeassistant.helpers.service import async_get_all_descriptions
|
||||
from homeassistant.helpers.state import AsyncTrackStates
|
||||
from homeassistant.helpers.system_info import async_get_system_info
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -45,6 +46,7 @@ ATTR_BASE_URL = "base_url"
|
||||
ATTR_EXTERNAL_URL = "external_url"
|
||||
ATTR_INTERNAL_URL = "internal_url"
|
||||
ATTR_LOCATION_NAME = "location_name"
|
||||
ATTR_INSTALLATION_TYPE = "installation_type"
|
||||
ATTR_REQUIRES_API_PASSWORD = "requires_api_password"
|
||||
ATTR_UUID = "uuid"
|
||||
ATTR_VERSION = "version"
|
||||
@ -181,6 +183,7 @@ class APIDiscoveryView(HomeAssistantView):
|
||||
"""Get discovery information."""
|
||||
hass = request.app["hass"]
|
||||
uuid = await hass.helpers.instance_id.async_get()
|
||||
system_info = await async_get_system_info(hass)
|
||||
|
||||
data = {
|
||||
ATTR_UUID: uuid,
|
||||
@ -188,6 +191,7 @@ class APIDiscoveryView(HomeAssistantView):
|
||||
ATTR_EXTERNAL_URL: None,
|
||||
ATTR_INTERNAL_URL: None,
|
||||
ATTR_LOCATION_NAME: hass.config.location_name,
|
||||
ATTR_INSTALLATION_TYPE: system_info[ATTR_INSTALLATION_TYPE],
|
||||
# always needs authentication
|
||||
ATTR_REQUIRES_API_PASSWORD: True,
|
||||
ATTR_VERSION: __version__,
|
||||
|
@ -2,6 +2,6 @@
|
||||
"domain": "apprise",
|
||||
"name": "Apprise",
|
||||
"documentation": "https://www.home-assistant.io/integrations/apprise",
|
||||
"requirements": ["apprise==0.8.8"],
|
||||
"requirements": ["apprise==0.8.9"],
|
||||
"codeowners": ["@caronc"]
|
||||
}
|
||||
|
@ -28,12 +28,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
|
||||
def get_service(hass, config, discovery_info=None):
|
||||
"""Get the Apprise notification service."""
|
||||
|
||||
# Create our Apprise Asset Object
|
||||
asset = apprise.AppriseAsset(async_mode=False)
|
||||
|
||||
# Create our Apprise Instance (reference our asset)
|
||||
a_obj = apprise.Apprise(asset=asset)
|
||||
a_obj = apprise.Apprise()
|
||||
|
||||
if config.get(CONF_FILE):
|
||||
# Sourced from a Configuration File
|
||||
|
@ -12,7 +12,8 @@
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "Vert"
|
||||
"host": "Vert",
|
||||
"port": ""
|
||||
},
|
||||
"description": "Vennligst skriv inn vertsnavnet eller IP-adressen til enheten."
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane."
|
||||
"already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
|
@ -23,6 +23,12 @@ CONFIG_SCHEMA = vol.Schema(
|
||||
|
||||
def setup(hass, config):
|
||||
"""Set up the Arduino component."""
|
||||
_LOGGER.warning(
|
||||
"The %s integration has been deprecated. Please move your "
|
||||
"configuration to the firmata integration. "
|
||||
"https://www.home-assistant.io/integrations/firmata",
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
port = config[DOMAIN][CONF_PORT]
|
||||
|
||||
|
@ -3,6 +3,6 @@
|
||||
"name": "Atag",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/atag/",
|
||||
"requirements": ["pyatag==0.3.3.4"],
|
||||
"requirements": ["pyatag==0.3.4.4"],
|
||||
"codeowners": ["@MatsNL"]
|
||||
}
|
||||
|
@ -16,5 +16,6 @@
|
||||
"title": "Verbinding maken met het apparaat"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": ""
|
||||
}
|
@ -11,10 +11,12 @@
|
||||
"user": {
|
||||
"data": {
|
||||
"email": "E-post (valgfritt)",
|
||||
"host": "Vert"
|
||||
"host": "Vert",
|
||||
"port": ""
|
||||
},
|
||||
"title": "Koble til enheten"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": ""
|
||||
}
|
@ -3,13 +3,18 @@ import asyncio
|
||||
import itertools
|
||||
import logging
|
||||
|
||||
from aiohttp import ClientError
|
||||
from aiohttp import ClientError, ClientResponseError
|
||||
from august.authenticator import ValidationResult
|
||||
from august.exceptions import AugustApiAIOHTTPError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_TIMEOUT, CONF_USERNAME
|
||||
from homeassistant.const import (
|
||||
CONF_PASSWORD,
|
||||
CONF_TIMEOUT,
|
||||
CONF_USERNAME,
|
||||
HTTP_UNAUTHORIZED,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@ -29,7 +34,7 @@ from .const import (
|
||||
MIN_TIME_BETWEEN_DETAIL_UPDATES,
|
||||
VERIFICATION_CODE_KEY,
|
||||
)
|
||||
from .exceptions import InvalidAuth, RequireValidation
|
||||
from .exceptions import CannotConnect, InvalidAuth, RequireValidation
|
||||
from .gateway import AugustGateway
|
||||
from .subscriber import AugustSubscriberMixin
|
||||
|
||||
@ -113,10 +118,7 @@ async def async_setup_august(hass, config_entry, august_gateway):
|
||||
await august_gateway.async_authenticate()
|
||||
except RequireValidation:
|
||||
await async_request_validation(hass, config_entry, august_gateway)
|
||||
return False
|
||||
except InvalidAuth:
|
||||
_LOGGER.error("Password is no longer valid. Please set up August again")
|
||||
return False
|
||||
raise
|
||||
|
||||
# We still use the configurator to get a new 2fa code
|
||||
# when needed since config_flow doesn't have a way
|
||||
@ -171,8 +173,30 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
try:
|
||||
await august_gateway.async_setup(entry.data)
|
||||
return await async_setup_august(hass, entry, august_gateway)
|
||||
except asyncio.TimeoutError as err:
|
||||
except ClientResponseError as err:
|
||||
if err.status == HTTP_UNAUTHORIZED:
|
||||
_async_start_reauth(hass, entry)
|
||||
return False
|
||||
|
||||
raise ConfigEntryNotReady from err
|
||||
except InvalidAuth:
|
||||
_async_start_reauth(hass, entry)
|
||||
return False
|
||||
except RequireValidation:
|
||||
return False
|
||||
except (CannotConnect, asyncio.TimeoutError) as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
|
||||
def _async_start_reauth(hass: HomeAssistant, entry: ConfigEntry):
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": "reauth"},
|
||||
data=entry.data,
|
||||
)
|
||||
)
|
||||
_LOGGER.error("Password is no longer valid. Please reauthenticate")
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
|
@ -4,7 +4,7 @@ import logging
|
||||
from august.authenticator import ValidationResult
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries, core
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_TIMEOUT, CONF_USERNAME
|
||||
|
||||
from .const import (
|
||||
@ -19,18 +19,8 @@ from .gateway import AugustGateway
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_LOGIN_METHOD, default="phone"): vol.In(LOGIN_METHODS),
|
||||
vol.Required(CONF_USERNAME): str,
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): vol.Coerce(int),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def async_validate_input(
|
||||
hass: core.HomeAssistant,
|
||||
data,
|
||||
august_gateway,
|
||||
):
|
||||
@ -79,6 +69,7 @@ class AugustConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Store an AugustGateway()."""
|
||||
self._august_gateway = None
|
||||
self.user_auth_details = {}
|
||||
self._needs_reset = False
|
||||
super().__init__()
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
@ -87,30 +78,45 @@ class AugustConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
self._august_gateway = AugustGateway(self.hass)
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
await self._august_gateway.async_setup(user_input)
|
||||
combined_inputs = {**self.user_auth_details, **user_input}
|
||||
await self._august_gateway.async_setup(combined_inputs)
|
||||
if self._needs_reset:
|
||||
self._needs_reset = False
|
||||
await self._august_gateway.async_reset_authentication()
|
||||
|
||||
try:
|
||||
info = await async_validate_input(
|
||||
self.hass,
|
||||
user_input,
|
||||
combined_inputs,
|
||||
self._august_gateway,
|
||||
)
|
||||
await self.async_set_unique_id(user_input[CONF_USERNAME])
|
||||
return self.async_create_entry(title=info["title"], data=info["data"])
|
||||
except CannotConnect:
|
||||
errors["base"] = "cannot_connect"
|
||||
except InvalidAuth:
|
||||
errors["base"] = "invalid_auth"
|
||||
except RequireValidation:
|
||||
self.user_auth_details = user_input
|
||||
self.user_auth_details.update(user_input)
|
||||
|
||||
return await self.async_step_validation()
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
errors["base"] = "unknown"
|
||||
|
||||
if not errors:
|
||||
self.user_auth_details.update(user_input)
|
||||
|
||||
existing_entry = await self.async_set_unique_id(
|
||||
combined_inputs[CONF_USERNAME]
|
||||
)
|
||||
if existing_entry:
|
||||
self.hass.config_entries.async_update_entry(
|
||||
existing_entry, data=info["data"]
|
||||
)
|
||||
await self.hass.config_entries.async_reload(existing_entry.entry_id)
|
||||
return self.async_abort(reason="reauth_successful")
|
||||
return self.async_create_entry(title=info["title"], data=info["data"])
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=DATA_SCHEMA, errors=errors
|
||||
step_id="user", data_schema=self._async_build_schema(), errors=errors
|
||||
)
|
||||
|
||||
async def async_step_validation(self, user_input=None):
|
||||
@ -135,3 +141,23 @@ class AugustConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return await self.async_step_user(user_input)
|
||||
|
||||
async def async_step_reauth(self, data):
|
||||
"""Handle configuration by re-auth."""
|
||||
self.user_auth_details = dict(data)
|
||||
self._needs_reset = True
|
||||
return await self.async_step_user()
|
||||
|
||||
def _async_build_schema(self):
|
||||
"""Generate the config flow schema."""
|
||||
base_schema = {
|
||||
vol.Required(CONF_LOGIN_METHOD, default="phone"): vol.In(LOGIN_METHODS),
|
||||
vol.Required(CONF_USERNAME): str,
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): vol.Coerce(int),
|
||||
}
|
||||
for key in self.user_auth_details:
|
||||
if key == CONF_PASSWORD or key not in base_schema:
|
||||
continue
|
||||
del base_schema[key]
|
||||
return vol.Schema(base_schema)
|
||||
|
@ -2,12 +2,18 @@
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
|
||||
from aiohttp import ClientError
|
||||
from aiohttp import ClientError, ClientResponseError
|
||||
from august.api_async import ApiAsync
|
||||
from august.authenticator_async import AuthenticationState, AuthenticatorAsync
|
||||
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_TIMEOUT, CONF_USERNAME
|
||||
from homeassistant.const import (
|
||||
CONF_PASSWORD,
|
||||
CONF_TIMEOUT,
|
||||
CONF_USERNAME,
|
||||
HTTP_UNAUTHORIZED,
|
||||
)
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
|
||||
from .const import (
|
||||
@ -32,29 +38,14 @@ class AugustGateway:
|
||||
self._access_token_cache_file = None
|
||||
self._hass = hass
|
||||
self._config = None
|
||||
self._api = None
|
||||
self._authenticator = None
|
||||
self._authentication = None
|
||||
|
||||
@property
|
||||
def authenticator(self):
|
||||
"""August authentication object from py-august."""
|
||||
return self._authenticator
|
||||
|
||||
@property
|
||||
def authentication(self):
|
||||
"""August authentication object from py-august."""
|
||||
return self._authentication
|
||||
self.api = None
|
||||
self.authenticator = None
|
||||
self.authentication = None
|
||||
|
||||
@property
|
||||
def access_token(self):
|
||||
"""Access token for the api."""
|
||||
return self._authentication.access_token
|
||||
|
||||
@property
|
||||
def api(self):
|
||||
"""August api object from py-august."""
|
||||
return self._api
|
||||
return self.authentication.access_token
|
||||
|
||||
def config_entry(self):
|
||||
"""Config entry."""
|
||||
@ -78,12 +69,12 @@ class AugustGateway:
|
||||
)
|
||||
self._config = conf
|
||||
|
||||
self._api = ApiAsync(
|
||||
self.api = ApiAsync(
|
||||
self._aiohttp_session, timeout=self._config.get(CONF_TIMEOUT)
|
||||
)
|
||||
|
||||
self._authenticator = AuthenticatorAsync(
|
||||
self._api,
|
||||
self.authenticator = AuthenticatorAsync(
|
||||
self.api,
|
||||
self._config[CONF_LOGIN_METHOD],
|
||||
self._config[CONF_USERNAME],
|
||||
self._config[CONF_PASSWORD],
|
||||
@ -93,30 +84,47 @@ class AugustGateway:
|
||||
),
|
||||
)
|
||||
|
||||
await self._authenticator.async_setup_authentication()
|
||||
await self.authenticator.async_setup_authentication()
|
||||
|
||||
async def async_authenticate(self):
|
||||
"""Authenticate with the details provided to setup."""
|
||||
self._authentication = None
|
||||
self.authentication = None
|
||||
try:
|
||||
self._authentication = await self.authenticator.async_authenticate()
|
||||
self.authentication = await self.authenticator.async_authenticate()
|
||||
if self.authentication.state == AuthenticationState.AUTHENTICATED:
|
||||
# Call the locks api to verify we are actually
|
||||
# authenticated because we can be authenticated
|
||||
# by have no access
|
||||
await self.api.async_get_operable_locks(self.access_token)
|
||||
except ClientResponseError as ex:
|
||||
if ex.status == HTTP_UNAUTHORIZED:
|
||||
raise InvalidAuth from ex
|
||||
|
||||
raise CannotConnect from ex
|
||||
except ClientError as ex:
|
||||
_LOGGER.error("Unable to connect to August service: %s", str(ex))
|
||||
raise CannotConnect from ex
|
||||
|
||||
if self._authentication.state == AuthenticationState.BAD_PASSWORD:
|
||||
if self.authentication.state == AuthenticationState.BAD_PASSWORD:
|
||||
raise InvalidAuth
|
||||
|
||||
if self._authentication.state == AuthenticationState.REQUIRES_VALIDATION:
|
||||
if self.authentication.state == AuthenticationState.REQUIRES_VALIDATION:
|
||||
raise RequireValidation
|
||||
|
||||
if self._authentication.state != AuthenticationState.AUTHENTICATED:
|
||||
_LOGGER.error(
|
||||
"Unknown authentication state: %s", self._authentication.state
|
||||
)
|
||||
if self.authentication.state != AuthenticationState.AUTHENTICATED:
|
||||
_LOGGER.error("Unknown authentication state: %s", self.authentication.state)
|
||||
raise InvalidAuth
|
||||
|
||||
return self._authentication
|
||||
return self.authentication
|
||||
|
||||
async def async_reset_authentication(self):
|
||||
"""Remove the cache file."""
|
||||
await self._hass.async_add_executor_job(self._reset_authentication)
|
||||
|
||||
def _reset_authentication(self):
|
||||
"""Remove the cache file."""
|
||||
if os.path.exists(self._access_token_cache_file):
|
||||
os.unlink(self._access_token_cache_file)
|
||||
|
||||
async def async_refresh_access_token_if_needed(self):
|
||||
"""Refresh the august access token if needed."""
|
||||
@ -130,4 +138,4 @@ class AugustGateway:
|
||||
self.authentication.access_token_expires,
|
||||
refreshed_authentication.access_token_expires,
|
||||
)
|
||||
self._authentication = refreshed_authentication
|
||||
self.authentication = refreshed_authentication
|
||||
|
@ -6,7 +6,8 @@
|
||||
"invalid_auth": "Invalid authentication"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "Account is already configured"
|
||||
"already_configured": "Account is already configured",
|
||||
"reauth_successful": "Re-authentication was successful"
|
||||
},
|
||||
"step": {
|
||||
"validation": {
|
||||
|
@ -1,7 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "El compte ja ha estat configurat"
|
||||
"already_configured": "El compte ja ha estat configurat",
|
||||
"reauth_successful": "Re-autenticaci\u00f3 realitzada correctament"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "No s'ha pogut connectar, torna-ho a provar",
|
||||
|
7
homeassistant/components/august/translations/el.json
Normal file
7
homeassistant/components/august/translations/el.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"reauth_successful": "\u0397 \u03b5\u03c0\u03b1\u03bd\u03b1\u03c0\u03b9\u03c3\u03c4\u03bf\u03c0\u03bf\u03af\u03b7\u03c3\u03b7 \u03ae\u03c4\u03b1\u03bd \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03ae\u03c2"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Account is already configured"
|
||||
"already_configured": "Account is already configured",
|
||||
"reauth_successful": "Re-authentication was successful"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Failed to connect, please try again",
|
||||
|
@ -1,7 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "La cuenta ya est\u00e1 configurada"
|
||||
"already_configured": "La cuenta ya est\u00e1 configurada",
|
||||
"reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "No se ha podido conectar, por favor, int\u00e9ntalo de nuevo.",
|
||||
|
7
homeassistant/components/august/translations/et.json
Normal file
7
homeassistant/components/august/translations/et.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"reauth_successful": "Taasautentimine \u00f5nnestus"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9"
|
||||
"already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9",
|
||||
"reauth_successful": "La r\u00e9-authentification a r\u00e9ussi"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Impossible de se connecter, veuillez r\u00e9essayer",
|
||||
|
@ -1,7 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'account \u00e8 gi\u00e0 configurato"
|
||||
"already_configured": "L'account \u00e8 gi\u00e0 configurato",
|
||||
"reauth_successful": "La riautenticazione ha avuto successo"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Impossibile connettersi, si prega di riprovare.",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user